Skip to content

Commit b355fa4

Browse files
committed
Update UI based on db change
1 parent 402f9c9 commit b355fa4

File tree

38 files changed

+1145
-754
lines changed

38 files changed

+1145
-754
lines changed

src/Bible.Alarm.Shared/Database/Migrations/Media/20250101000001_AddHarvestTypeToSectionLanguages.Designer.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/Bible.Alarm.Shared/Database/Migrations/Media/20250101000001_AddHarvestTypeToSectionLanguages.cs

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/Bible.Alarm.Shared/Helpers/MediaReader.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ public async Task<SortedDictionary<int, BiblePublicationSection>> GetBiblePublic
3434
var sectionsIndex = Path.Combine(root, "Audio", "Bible", languageCode, versionCode, "sections.json");
3535
var biblePublicationSections = await File.ReadAllTextAsync(sectionsIndex);
3636
var sections = JsonSerializer.Deserialize<IEnumerable<BiblePublicationSection>>(biblePublicationSections)!;
37-
// Use BookNum if available (from UrlParams), otherwise try to parse SectionCode
37+
// Use SectionCode to get section number
3838
return new SortedDictionary<int, BiblePublicationSection>(sections
39-
.Where(s => s.BookNum.HasValue || int.TryParse(s.SectionCode, out _))
40-
.ToDictionary(x => x.BookNum ?? (int.TryParse(x.SectionCode, out var num) ? num : 0), x => x));
39+
.Where(s => int.TryParse(s.SectionCode, out _))
40+
.ToDictionary(x => int.TryParse(x.SectionCode, out var num) ? num : 0, x => x));
4141
}
4242

4343
public async Task<SortedDictionary<int, BiblePublicationTrack>> GetBiblePublicationTracks(string languageCode, string versionCode,

src/Bible.Alarm.Shared/Models/Media/BiblePublications/BiblePublicationSection.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,17 @@ public sealed class BiblePublicationSection : IComparable
4343
public List<BiblePublicationTrack> Tracks { get; set; } = [];
4444

4545
/// <summary>
46-
/// Helper property to get the booknum value from UrlParams.
47-
/// Returns null if not found or cannot be parsed.
46+
/// Gets the section number by parsing SectionCode.
47+
/// Returns null if SectionCode cannot be parsed as an integer.
48+
/// Note: UrlParams["booknum"] is only used for URL construction, not for business logic.
4849
/// </summary>
49-
public int? BookNum
50+
public int? GetSectionNumber()
5051
{
51-
get
52+
if (int.TryParse(SectionCode, out var sectionNumber))
5253
{
53-
var booknumParam = UrlParams.FirstOrDefault(p => p.Key.Equals("booknum", StringComparison.OrdinalIgnoreCase));
54-
if (booknumParam != null && int.TryParse(booknumParam.Value, out var booknum))
55-
{
56-
return booknum;
57-
}
58-
return null;
54+
return sectionNumber;
5955
}
56+
return null;
6057
}
6158

6259
public int CompareTo(object obj)

src/Bible.Alarm.Shared/Models/Schedule/AlarmSchedule.cs

Lines changed: 115 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Threading.Tasks;
99
using Bible.Alarm.Shared.Helpers;
1010
using Bible.Alarm.Shared.Models.Enums;
11+
using Bible.Alarm.Shared.Models.Media.BiblePublications;
1112
using Bible.Alarm.Shared.Services.Media.Interfaces;
1213
using Microsoft.EntityFrameworkCore;
1314
using Quartz;
@@ -163,11 +164,6 @@ public static async Task<AlarmSchedule> GetSampleSchedule(bool isNew, IBiblePubl
163164
throw new InvalidOperationException("No Bible publications found in database");
164165
}
165166

166-
// Default to English, fallback to first available language with a sectioned publication
167-
const string DefaultLanguageCode = "E";
168-
string? bibleLanguageCode = null;
169-
string? biblePublicationCode = null;
170-
171167
// Priority codes for Bible publications (lower = higher priority)
172168
// Prefer nwt (2013 NWT) over bi12 (1984 NWT)
173169
string[] priorityPublicationCodes = ["nwt", "bi12"];
@@ -182,12 +178,37 @@ int GetPublicationSortPriority(string code)
182178
}
183179
return priorityPublicationCodes.Length; // Others come after priority publications
184180
}
181+
182+
// Optimize: Load publications with sections in one call to get both publication info and sections
183+
// For new schedules, prefer "nwt" with English "E", then fallback to first available language with a sectioned publication
184+
const string DefaultLanguageCode = "E";
185+
const string PreferredPublicationCode = "nwt";
186+
string? bibleLanguageCode = null;
187+
string? biblePublicationCode = null;
188+
BiblePublication? selectedBible = null;
185189

186-
// Try English first
190+
// Try English "E" with "nwt" first (for new schedules)
187191
if (bibleLanguages.ContainsKey(DefaultLanguageCode))
188192
{
189193
var englishPublications = await biblePublicationService.GetByLanguageCodeAsync(DefaultLanguageCode);
190-
if (englishPublications != null)
194+
if (englishPublications != null && englishPublications.ContainsKey(PreferredPublicationCode))
195+
{
196+
// Try "nwt" first
197+
if (PublicationTypeHelper.HasSectionStructure(PreferredPublicationCode))
198+
{
199+
var biblePub = await biblePublicationService.GetByLanguageAndCodeWithSectionsAsync(
200+
DefaultLanguageCode, PreferredPublicationCode);
201+
if (biblePub != null && biblePub.Sections != null && biblePub.Sections.Count > 0)
202+
{
203+
bibleLanguageCode = DefaultLanguageCode;
204+
biblePublicationCode = PreferredPublicationCode;
205+
selectedBible = biblePub;
206+
}
207+
}
208+
}
209+
210+
// If "nwt" not available, try other English publications
211+
if (selectedBible == null && englishPublications != null)
191212
{
192213
// Sort publications by priority: nwt first, then bi12, then others
193214
var sortedPublications = englishPublications
@@ -199,16 +220,23 @@ int GetPublicationSortPriority(string code)
199220
{
200221
if (PublicationTypeHelper.HasSectionStructure(pub.Key))
201222
{
202-
bibleLanguageCode = DefaultLanguageCode;
203-
biblePublicationCode = pub.Key;
204-
break;
223+
// Load with sections in one call - this includes Category
224+
var biblePub = await biblePublicationService.GetByLanguageAndCodeWithSectionsAsync(
225+
DefaultLanguageCode, pub.Key);
226+
if (biblePub != null && biblePub.Sections != null && biblePub.Sections.Count > 0)
227+
{
228+
bibleLanguageCode = DefaultLanguageCode;
229+
biblePublicationCode = pub.Key;
230+
selectedBible = biblePub;
231+
break;
232+
}
205233
}
206234
}
207235
}
208236
}
209237

210238
// Fallback: find any language with a sectioned publication
211-
if (bibleLanguageCode == null)
239+
if (selectedBible == null)
212240
{
213241
foreach (var lang in bibleLanguages)
214242
{
@@ -228,25 +256,32 @@ int GetPublicationSortPriority(string code)
228256
{
229257
if (PublicationTypeHelper.HasSectionStructure(pub.Key))
230258
{
231-
bibleLanguageCode = lang.Key;
232-
biblePublicationCode = pub.Key;
233-
break;
259+
// Load with sections in one call - this includes Category
260+
var biblePub = await biblePublicationService.GetByLanguageAndCodeWithSectionsAsync(
261+
lang.Key, pub.Key);
262+
if (biblePub != null && biblePub.Sections != null && biblePub.Sections.Count > 0)
263+
{
264+
bibleLanguageCode = lang.Key;
265+
biblePublicationCode = pub.Key;
266+
selectedBible = biblePub;
267+
break;
268+
}
234269
}
235270
}
236271

237-
if (bibleLanguageCode != null)
272+
if (selectedBible != null)
238273
{
239274
break;
240275
}
241276
}
242277
}
243278

244-
if (bibleLanguageCode == null || biblePublicationCode == null)
279+
if (selectedBible == null || bibleLanguageCode == null || biblePublicationCode == null)
245280
{
246281
throw new InvalidOperationException("No sectioned Bible publication found in database for sample schedule");
247282
}
248283

249-
// Get first available melody music from database
284+
// Get first available melody music from database that has tracks
250285
var melodyQueryStart = DateTime.UtcNow;
251286
var melodyReleases = await melodyMusicService.GetAllAsync();
252287
Log.Information("[PERF] GetSampleSchedule: Melody releases query took {ElapsedMs}ms", (DateTime.UtcNow - melodyQueryStart).TotalMilliseconds);
@@ -256,8 +291,22 @@ int GetPublicationSortPriority(string code)
256291
throw new InvalidOperationException("No melody music found in database");
257292
}
258293

259-
var firstMelody = melodyReleases.FirstOrDefault();
260-
var melodyPublicationCode = firstMelody.Key;
294+
// Find a melody music publication that has tracks
295+
string? melodyPublicationCode = null;
296+
foreach (var melody in melodyReleases)
297+
{
298+
var musicWithTracks = await melodyMusicService.GetByCodeWithTracksAsync(melody.Key);
299+
if (musicWithTracks?.Tracks != null && musicWithTracks.Tracks.Count > 0)
300+
{
301+
melodyPublicationCode = melody.Key;
302+
break;
303+
}
304+
}
305+
306+
if (melodyPublicationCode == null)
307+
{
308+
throw new InvalidOperationException("No melody music with tracks found in database");
309+
}
261310

262311
// Create sample schedule disabled by default - user must explicitly enable it
263312
var sample = new AlarmSchedule
@@ -284,33 +333,38 @@ int GetPublicationSortPriority(string code)
284333
}
285334
};
286335

287-
var bibleQueryStartTime = DateTime.UtcNow;
288-
var bible = await biblePublicationService.GetByLanguageAndCodeWithSectionsAsync(
289-
sample.BiblePublicationSchedule.LanguageCode,
290-
sample.BiblePublicationSchedule.PublicationCode);
291-
var bibleQueryElapsed = (DateTime.UtcNow - bibleQueryStartTime).TotalMilliseconds;
292-
Log.Information("[PERF] GetSampleSchedule: Bible sections query took {ElapsedMs}ms", bibleQueryElapsed);
293-
294-
if (bible == null)
295-
{
296-
throw new InvalidOperationException("Bible publication not found for sample schedule");
297-
}
298-
299-
if (bible.Sections == null || bible.Sections.Count == 0)
336+
// Use the already-loaded bible publication (no additional DB call needed)
337+
// selectedBible is guaranteed to be non-null here due to the check above
338+
if (selectedBible.Sections == null || selectedBible.Sections.Count == 0)
300339
{
301340
throw new InvalidOperationException($"No sections found for Bible publication {biblePublicationCode}");
302341
}
303342

304343
// Use Random.Shared for thread-safe random number generation
305344
// Safe for non-cryptographic use (selecting sample sections/tracks)
306-
var section = bible.Sections[Random.Shared.Next(bible.Sections.Count)];
345+
// Only select sections that have tracks
346+
var sectionsWithTracks = selectedBible.Sections
347+
.Where(s => s.Tracks != null && s.Tracks.Count > 0)
348+
.ToList();
349+
350+
if (sectionsWithTracks.Count == 0)
351+
{
352+
throw new InvalidOperationException($"No sections with tracks found for Bible publication {biblePublicationCode}");
353+
}
354+
355+
var section = sectionsWithTracks[Random.Shared.Next(sectionsWithTracks.Count)];
307356
if (sample.BiblePublicationSchedule == null)
308357
{
309358
throw new InvalidOperationException("BiblePublicationSchedule is null in sample schedule");
310359
}
311360

312361
// Use SectionCode directly to match media index db
313362
sample.BiblePublicationSchedule.SectionCode = section.SectionCode;
363+
364+
// Get the first track of the selected section (book)
365+
// We already verified the section has tracks above
366+
var firstTrack = section.Tracks!.OrderBy(t => t.Number).First();
367+
sample.BiblePublicationSchedule.TrackNumber = firstTrack.Number;
314368

315369
if (sample.Music == null)
316370
{
@@ -322,13 +376,38 @@ int GetPublicationSortPriority(string code)
322376
var musicQueryElapsed = (DateTime.UtcNow - musicQueryStartTime).TotalMilliseconds;
323377
Log.Information("[PERF] GetSampleSchedule: Music tracks query took {ElapsedMs}ms", musicQueryElapsed);
324378

325-
if (music == null)
379+
if (music == null || music.Publication == null)
326380
{
327381
throw new InvalidOperationException("Melody music not found for sample schedule");
328382
}
329383

330-
var track = music.Tracks[Random.Shared.Next(music.Tracks.Count)];
331-
sample.Music.TrackNumber = track.Number;
384+
// Melody music is now sectioned (e.g., iam has sections like "iam-1", "iam-2")
385+
// Select a random section, then a random track from that section
386+
if (music.Publication.Sections == null || music.Publication.Sections.Count == 0)
387+
{
388+
// Fallback: if no sections, try direct tracks (for backward compatibility)
389+
if (music.Tracks == null || music.Tracks.Count == 0)
390+
{
391+
throw new InvalidOperationException($"No sections or tracks found for melody music publication {sample.Music.PublicationCode}");
392+
}
393+
394+
var track = music.Tracks[Random.Shared.Next(music.Tracks.Count)];
395+
sample.Music.TrackNumber = track.Number;
396+
}
397+
else
398+
{
399+
// Select a random section
400+
var randomSection = music.Publication.Sections[Random.Shared.Next(music.Publication.Sections.Count)];
401+
402+
if (randomSection.Tracks == null || randomSection.Tracks.Count == 0)
403+
{
404+
throw new InvalidOperationException($"No tracks found in section {randomSection.SectionCode} for melody music publication {sample.Music.PublicationCode}");
405+
}
406+
407+
// Select a random track from the selected section
408+
var randomTrack = randomSection.Tracks[Random.Shared.Next(randomSection.Tracks.Count)];
409+
sample.Music.TrackNumber = randomTrack.Number;
410+
}
332411

333412
var totalElapsed = (DateTime.UtcNow - startTime).TotalMilliseconds;
334413
Log.Information("[PERF] GetSampleSchedule: Completed in {ElapsedMs}ms", totalElapsed);

0 commit comments

Comments
 (0)