Skip to content
Merged
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
28 changes: 23 additions & 5 deletions Services/ElectricityPriceFetchService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ public class ElectricityPriceFetchService : BackgroundService
private readonly IServiceScopeFactory _scopeFactory;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<ElectricityPriceFetchService> _logger;
private readonly ILoggerFactory _loggerFactory;

public ElectricityPriceFetchService(
IServiceScopeFactory scopeFactory,
IHttpClientFactory httpClientFactory,
ILogger<ElectricityPriceFetchService> logger)
ILogger<ElectricityPriceFetchService> logger,
ILoggerFactory loggerFactory)
{
_scopeFactory = scopeFactory;
_httpClientFactory = httpClientFactory;
_logger = logger;
_loggerFactory = loggerFactory;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
Expand Down Expand Up @@ -69,7 +72,8 @@ private async Task FetchDailyRefreshAsync(CancellationToken stoppingToken)
var now = DateTime.UtcNow;
var currYear = now.Year;

// HOURLY: next day
// HOURLY: today (refreshes any slots published after startup) + next day
await FetchAndStoreAsync("HOURLY", now, stoppingToken);
await FetchAndStoreAsync("HOURLY", now.AddDays(1), stoppingToken);
// DAILY + MONTHLY: refresh current year
await FetchAndStoreAsync("DAILY", new DateTime(currYear, 12, 31, 0, 0, 0, DateTimeKind.Utc), stoppingToken);
Expand All @@ -82,8 +86,8 @@ private async Task FetchAndStoreAsync(string resolution, DateTime date, Cancella
try
{
var httpClient = _httpClientFactory.CreateClient();
var nordPoolService = new NordPoolService(httpClient,
Microsoft.Extensions.Logging.Abstractions.NullLogger<NordPoolService>.Instance);
// Use real logger so NordPool parse warnings (e.g. missing JSON keys) are visible.
var nordPoolService = new NordPoolService(httpClient, _loggerFactory.CreateLogger<NordPoolService>());

var priceResponse = await nordPoolService.FetchPricesAsync(resolution, date, Areas.ToList(), "NOK");
if (priceResponse == null)
Expand All @@ -92,16 +96,29 @@ private async Task FetchAndStoreAsync(string resolution, DateTime date, Cancella
return;
}

if (priceResponse.Areas.Count == 0)
{
_logger.LogWarning("ElectricityPriceFetchService: NordPool returned empty areas for {Resolution} on {Date:yyyy-MM-dd} — no entries stored.", resolution, date);
return;
}

using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var fetchedAt = DateTime.UtcNow;
var totalStored = 0;

// Collect all delivery starts from the response so we can bulk-fetch existing rows
// in a single query per area instead of one query per entry (N+1).
foreach (var (area, areaPrices) in priceResponse.Areas)
{
if (!Areas.Contains(area)) continue;

if (areaPrices.Values.Count == 0)
{
_logger.LogWarning("ElectricityPriceFetchService: 0 entries for area {Area} {Resolution} on {Date:yyyy-MM-dd}.", area, resolution, date);
continue;
}

var incomingStarts = areaPrices.Values
.Select(e => e.Start)
.ToHashSet();
Expand Down Expand Up @@ -134,11 +151,12 @@ private async Task FetchAndStoreAsync(string resolution, DateTime date, Cancella
FetchedAt = fetchedAt,
});
}
totalStored++;
}
}

await db.SaveChangesAsync(stoppingToken);
_logger.LogInformation("ElectricityPriceFetchService: stored {Resolution} prices for {Date:yyyy-MM-dd}", resolution, date);
_logger.LogInformation("ElectricityPriceFetchService: stored {Count} {Resolution} entries for {Date:yyyy-MM-dd}", totalStored, resolution, date);
}
catch (Exception ex)
{
Expand Down
Loading