Skip to content

Commit 254c79a

Browse files
authored
Merge pull request #30 from dddsw/auto-update-from-sessionize
Auto update from sessionize
2 parents 68b15b1 + 46d8f6e commit 254c79a

8 files changed

Lines changed: 174 additions & 98 deletions

File tree

PocketDDD.BlazorClient/PocketDDD.BlazorClient/Services/FakePocketDDDApiService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public void SetUserAuthToken(string token)
2525
return
2626
new EventDataResponseDTO
2727
{
28-
Version = 1,
28+
Id = 1,
29+
Version = 0, //Set to 0 so if we ever connect this to a real API then it will update!
2930
TimeSlots = new[]
3031
{
3132
new TimeSlotDTO

PocketDDD.Server/PocketDDD.Server.DB/Migrations/2025_SeedData.sql

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@ delete TimeSlots
88
delete Tracks
99
delete EventDetail
1010

11-
12-
GO
13-
14-
-- Reset the identity columns
15-
DBCC CHECKIDENT ('[Tracks]', RESEED, 0);
16-
DBCC CHECKIDENT ('[TimeSlots]', RESEED, 0);
17-
DBCC CHECKIDENT ('[Sessions]', RESEED, 0);
18-
19-
-- There is hardcoding to EventDetail ID 1 so we reset to 1 not 0
20-
DBCC CHECKIDENT ('[EventDetail]', RESEED, 1); -- Use if this is a brand new table that has never been used before
21-
--DBCC CHECKIDENT ('[EventDetail]', RESEED, 0); -- Use if this is an empty table that used to have rows
22-
2311
GO
2412

2513
-- Add 2025 Sessionize ID
Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,79 @@
11
using Microsoft.EntityFrameworkCore;
2+
using Microsoft.Extensions.Caching.Memory;
3+
using Microsoft.Extensions.Logging;
24
using PocketDDD.Server.DB;
3-
using PocketDDD.Server.Model.DTOs;
4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Text;
8-
using System.Threading.Tasks;
95
using PocketDDD.Shared.API.RequestDTOs;
106
using PocketDDD.Shared.API.ResponseDTOs;
117

128
namespace PocketDDD.Server.Services;
13-
public class EventDataService
9+
10+
public class EventDataService(PocketDDDContext dbContext, IMemoryCache memoryCache, ILogger<EventDataService> logger)
1411
{
15-
private readonly PocketDDDContext dbContext;
12+
private const string FetchLatestEventDataCacheKey = nameof(FetchLatestEventData);
13+
private const string FetchCurrentEventDetailIdCacheKey = nameof(FetchCurrentEventDetailId);
1614

17-
public EventDataService(PocketDDDContext dbContext)
15+
public async Task<int> FetchCurrentEventDetailId()
1816
{
19-
this.dbContext = dbContext;
17+
if (memoryCache.TryGetValue<int?>(FetchCurrentEventDetailIdCacheKey, out var cachedEventDetailId))
18+
return cachedEventDetailId!.Value;
19+
20+
cachedEventDetailId = await dbContext.EventDetail.MaxAsync(e => e.Id);
21+
memoryCache.Set(FetchCurrentEventDetailIdCacheKey, cachedEventDetailId, TimeSpan.FromMinutes(5));
22+
23+
return cachedEventDetailId.Value;
2024
}
2125

22-
public async Task<EventDataResponseDTO?> FetchLatestEventData(EventDataUpdateRequestDTO requestDTO)
26+
public async Task<EventDataResponseDTO?> FetchLatestEventData(EventDataUpdateRequestDTO requestDto)
2327
{
24-
var eventDetails = await dbContext.EventDetail
25-
.Include(x => x.TimeSlots)
26-
.Include(x => x.Tracks)
27-
.Include(x => x.Sessions)
28-
.SingleAsync(x => x.Id == 1);
29-
30-
if (requestDTO.Version == eventDetails!.Version)
28+
var currentEventDetailId = await FetchCurrentEventDetailId();
29+
30+
if (memoryCache.TryGetValue<EventDataResponseDTO?>(FetchLatestEventDataCacheKey, out var latestEventData))
31+
{
32+
logger.LogDebug("Retrieved latest event data from the cache {eventData}", latestEventData);
33+
}
34+
else
35+
{
36+
logger.LogDebug("No event data in the cache, retrieving from the db");
37+
latestEventData = await dbContext.EventDetail
38+
.AsNoTracking()
39+
.AsSingleQuery()
40+
.Select(eventDetails => new EventDataResponseDTO
41+
{
42+
Id = eventDetails.Id,
43+
Version = eventDetails.Version,
44+
TimeSlots = eventDetails.TimeSlots.Select(ts => new TimeSlotDTO
45+
{
46+
Id = ts.Id,
47+
Info = ts.Info,
48+
From = ts.From,
49+
To = ts.To
50+
}).ToList(),
51+
Tracks = eventDetails.Tracks.Select(t => new TrackDTO
52+
{
53+
Id = t.Id,
54+
Name = t.Name,
55+
RoomName = t.RoomName
56+
}).ToList(),
57+
Sessions = eventDetails.Sessions.Select(s => new SessionDTO
58+
{
59+
Id = s.Id,
60+
Title = s.Title,
61+
ShortDescription = s.ShortDescription,
62+
FullDescription = s.FullDescription,
63+
Speaker = s.Speaker,
64+
TimeSlotId = s.TimeSlot.Id,
65+
TrackId = s.Track.Id
66+
}).ToList()
67+
})
68+
.SingleAsync(e => e.Id == currentEventDetailId);
69+
70+
memoryCache.Set(FetchLatestEventDataCacheKey, latestEventData, TimeSpan.FromMinutes(5));
71+
logger.LogDebug("Updated the latest event data in the cache {eventData}", latestEventData);
72+
}
73+
74+
if (requestDto.Version >= latestEventData!.Version)
3175
return null;
3276

33-
var dtoResponse = new EventDataResponseDTO
34-
{
35-
Version = eventDetails.Version,
36-
TimeSlots = eventDetails.TimeSlots.Select(ts => new TimeSlotDTO
37-
{
38-
Id = ts.Id,
39-
Info = ts.Info,
40-
From = ts.From,
41-
To = ts.To
42-
}).ToList(),
43-
Tracks = eventDetails.Tracks.Select(t => new TrackDTO
44-
{
45-
Id = t.Id,
46-
Name = t.Name,
47-
RoomName = t.RoomName
48-
}).ToList(),
49-
Sessions = eventDetails.Sessions.Select(s => new SessionDTO
50-
{
51-
Id = s.Id,
52-
Title = s.Title,
53-
ShortDescription = s.ShortDescription,
54-
FullDescription = s.FullDescription,
55-
Speaker = s.Speaker,
56-
TimeSlotId = s.TimeSlot.Id,
57-
TrackId = s.Track.Id
58-
}).ToList()
59-
};
60-
61-
return dtoResponse;
77+
return latestEventData;
6278
}
6379
}

PocketDDD.Server/PocketDDD.Server.Services/RegistrationService.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,20 @@
1-
using PocketDDD.Server.DB;
1+
using System.Security.Cryptography;
2+
using PocketDDD.Server.DB;
23
using PocketDDD.Server.Model.DBModel;
3-
using PocketDDD.Server.Model.DTOs;
4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Security.Cryptography;
8-
using System.Text;
9-
using System.Threading.Tasks;
104
using PocketDDD.Shared.API.RequestDTOs;
115
using PocketDDD.Shared.API.ResponseDTOs;
126

137
namespace PocketDDD.Server.Services;
14-
public class RegistrationService
15-
{
16-
private readonly PocketDDDContext dbContext;
17-
18-
public RegistrationService(PocketDDDContext dbContext)
19-
{
20-
this.dbContext = dbContext;
21-
}
228

9+
public class RegistrationService(PocketDDDContext dbContext, EventDataService eventDataService)
10+
{
2311
public async Task<LoginResultDTO> Register(RegisterDTO dto)
2412
{
13+
var currentEventDetailId = await eventDataService.FetchCurrentEventDetailId();
14+
2515
var user = new User
2616
{
27-
EventDetailId = 1,
17+
EventDetailId = currentEventDetailId,
2818
Name = dto.Name,
2919
Token = GenerateBearerToken(),
3020
EventScore = 1

PocketDDD.Server/PocketDDD.Server.Services/SessionizeService.cs

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,46 @@
1-
using Microsoft.EntityFrameworkCore;
1+
using System.Net.Http.Json;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.Extensions.Logging;
24
using PocketDDD.Server.DB;
35
using PocketDDD.Server.Model.DBModel;
46
using PocketDDD.Server.Model.Sessionize;
5-
using System.Net.Http.Json;
7+
using Session = PocketDDD.Server.Model.DBModel.Session;
68

79
namespace PocketDDD.Server.Services;
810

911
public class SessionizeService
1012
{
11-
private readonly HttpClient httpClient;
1213
private readonly PocketDDDContext dbContext;
14+
private readonly HttpClient httpClient;
1315

14-
public SessionizeService(HttpClient httpClient, PocketDDDContext dbContext)
16+
public SessionizeService(HttpClient httpClient, PocketDDDContext dbContext, ILogger<SessionizeService> logger)
1517
{
18+
Logger = logger;
1619
this.httpClient = httpClient;
1720
this.dbContext = dbContext;
1821
httpClient.BaseAddress = new Uri("https://sessionize.com/api/v2/");
1922
}
2023

24+
private ILogger<SessionizeService> Logger { get; }
25+
2126
public async Task UpdateFromSessionize()
2227
{
23-
var dbEvent = await dbContext.EventDetail.SingleAsync(x => x.Id == 1);
28+
Logger.LogInformation("Looking for event detail in database");
2429

30+
var dbEvent = await dbContext.EventDetail.OrderBy(x => x.Id).LastAsync();
2531
var sessionizeEventId = dbEvent.SessionizeId;
32+
33+
Logger.LogInformation("About to get data from Sessionize API");
34+
2635
var sessionizeEvent = await httpClient.GetFromJsonAsync<SessionizeEvent>($"{sessionizeEventId}/view/All");
2736

2837
if (sessionizeEvent is null)
2938
throw new ArgumentNullException(nameof(sessionizeEvent));
3039

31-
var dbTracks = await dbContext.Tracks.ToListAsync();
40+
Logger.LogInformation("Information retrieved from Sessionize API");
41+
Logger.LogInformation("Looking for changes to rooms");
42+
43+
var dbTracks = await dbContext.Tracks.Where(track => track.EventDetail.Id == dbEvent.Id).ToListAsync();
3244
foreach (var item in sessionizeEvent.rooms)
3345
{
3446
var dbTrack = dbTracks.SingleOrDefault(x => x.SessionizeId == item.id);
@@ -46,10 +58,21 @@ public async Task UpdateFromSessionize()
4658
dbTrack.Name = $"Track {item.sort}";
4759
}
4860

49-
await dbContext.SaveChangesAsync();
61+
if (dbContext.ChangeTracker.HasChanges())
62+
{
63+
dbEvent.Version++;
64+
Logger.LogInformation("Updating db with changes to rooms");
65+
await dbContext.SaveChangesAsync();
66+
}
67+
else
68+
{
69+
Logger.LogInformation("No changes to rooms were detected");
70+
}
5071

72+
Logger.LogInformation("Looking for changes to time slots and breaks");
5173

52-
var dbTimeSlots = await dbContext.TimeSlots.ToListAsync();
74+
var dbTimeSlots = await dbContext.TimeSlots.Where(timeSlot => timeSlot.EventDetail.Id == dbEvent.Id)
75+
.ToListAsync();
5376
var sessionizeTimeSlots = sessionizeEvent.sessions
5477
.Select(x => (x.startsAt, x.endsAt, x.isServiceSession,
5578
serviceSessionDetails: x.isServiceSession ? x.title : null))
@@ -65,17 +88,28 @@ public async Task UpdateFromSessionize()
6588
{
6689
EventDetail = dbEvent,
6790
From = item.startsAt,
68-
To = item.endsAt,
69-
Info = item.isServiceSession ? item.serviceSessionDetails : null
91+
To = item.endsAt
7092
};
7193
dbContext.TimeSlots.Add(dbTimeSlot);
7294
}
95+
96+
dbTimeSlot.Info = item.isServiceSession ? item.serviceSessionDetails : null;
7397
}
7498

75-
await dbContext.SaveChangesAsync();
99+
if (dbContext.ChangeTracker.HasChanges())
100+
{
101+
dbEvent.Version++;
102+
Logger.LogInformation("Updating db with changes to time slots and breaks");
103+
await dbContext.SaveChangesAsync();
104+
}
105+
else
106+
{
107+
Logger.LogInformation("No changes to time slots or breaks were detected");
108+
}
76109

110+
Logger.LogInformation("Looking for changes to sessions");
77111

78-
var dbSessions = await dbContext.Sessions.ToListAsync();
112+
var dbSessions = await dbContext.Sessions.Where(session => session.EventDetail.Id == dbEvent.Id).ToListAsync();
79113
var speakers = sessionizeEvent.speakers;
80114
dbTracks = await dbContext.Tracks.ToListAsync();
81115
dbTimeSlots = await dbContext.TimeSlots.ToListAsync();
@@ -89,7 +123,7 @@ public async Task UpdateFromSessionize()
89123
var dbSession = dbSessions.SingleOrDefault(x => x.SessionizeId == sessionizeId);
90124
if (dbSession == null)
91125
{
92-
dbSession = new Model.DBModel.Session
126+
dbSession = new Session
93127
{
94128
SessionizeId = sessionizeId,
95129
EventDetail = dbEvent,
@@ -106,7 +140,16 @@ public async Task UpdateFromSessionize()
106140
dbSession.TimeSlot = GetTimeSlot(dbTimeSlots, item.startsAt, item.endsAt);
107141
}
108142

109-
await dbContext.SaveChangesAsync();
143+
if (dbContext.ChangeTracker.HasChanges())
144+
{
145+
dbEvent.Version++;
146+
Logger.LogInformation("Updating db with changes to sessions");
147+
await dbContext.SaveChangesAsync();
148+
}
149+
else
150+
{
151+
Logger.LogInformation("No changes to sessions were detected");
152+
}
110153
}
111154

112155
private string GetSpeakers(List<Speaker> speakers, List<string> speakerIds)

PocketDDD.Server/PocketDDD.Server.WebAPI/Program.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.EntityFrameworkCore;
22
using PocketDDD.Server.DB;
33
using PocketDDD.Server.Services;
4+
using PocketDDD.Server.WebAPI;
45
using PocketDDD.Server.WebAPI.Authentication;
56

67
var corsPolicy = "corsPolicy";
@@ -28,6 +29,8 @@
2829
options => options.UseSqlServer("name=ConnectionStrings:PocketDDDContext")
2930
);
3031

32+
builder.Services.AddMemoryCache();
33+
3134
builder.Services.AddScoped<RegistrationService>();
3235
builder.Services.AddScoped<UserService>();
3336
builder.Services.AddScoped<FeedbackService>();
@@ -37,6 +40,8 @@
3740

3841
builder.Services.AddHttpClient<SessionizeService>();
3942

43+
builder.Services.AddHostedService<UpdateFromSessionizeBackgroundService>();
44+
4045
builder.Services.AddAuthentication()
4146
.AddScheme<UserIsRegisteredOptions, UserIsRegisteredAuthHandler>(UserIsRegisteredAuthHandler.SchemeName, null);
4247

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using PocketDDD.Server.Services;
2+
3+
namespace PocketDDD.Server.WebAPI;
4+
5+
public class UpdateFromSessionizeBackgroundService(
6+
IServiceProvider services,
7+
ILogger<UpdateFromSessionizeBackgroundService> logger)
8+
: BackgroundService
9+
{
10+
private ILogger<UpdateFromSessionizeBackgroundService> Logger { get; } = logger;
11+
private IServiceProvider Services { get; } = services;
12+
13+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
14+
{
15+
Logger.LogInformation("Update from Sessionize background task started.");
16+
17+
while (!stoppingToken.IsCancellationRequested)
18+
{
19+
Logger.LogInformation("About to update from Sessionize.");
20+
try
21+
{
22+
using var scope = Services.CreateScope();
23+
24+
var sessionizeService = scope.ServiceProvider.GetRequiredService<SessionizeService>();
25+
await sessionizeService.UpdateFromSessionize();
26+
27+
Logger.LogInformation("Update from Sessionize complete.");
28+
}
29+
catch (Exception e)
30+
{
31+
Logger.LogError(e, "Update from Sessionize failed.");
32+
}
33+
34+
await Task.Delay(TimeSpan.FromMinutes(30), stoppingToken);
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)