diff --git a/JWLMerge.BackupFileServices/Helpers/NotesExporter.cs b/JWLMerge.BackupFileServices/Helpers/NotesExporter.cs index 707ac1d..d69156d 100644 --- a/JWLMerge.BackupFileServices/Helpers/NotesExporter.cs +++ b/JWLMerge.BackupFileServices/Helpers/NotesExporter.cs @@ -36,16 +36,27 @@ internal sealed class NotesExporter if (location?.BookNumber == null) { + // not from a Bible continue; } int? colorCode = null; + int? startToken = null; + int? endToken = null; + if (note.UserMarkId != null) { var userMark = backupFile.Database.FindUserMark(note.UserMarkId.Value); if (userMark != null) { colorCode = userMark.ColorIndex; + + var blockRanges = backupFile.Database.FindBlockRanges(userMark.UserMarkId); + if (blockRanges != null) + { + startToken = blockRanges.First().StartToken; + endToken = blockRanges.First().EndToken; + } } } @@ -53,9 +64,12 @@ internal sealed class NotesExporter { ChapterNumber = location.ChapterNumber, VerseNumber = note.BlockIdentifier, + StartTokenInVerse = startToken, + EndTokenInVerse = endToken, NoteTitle = note.Title?.Trim(), NoteContent = note.Content?.Trim(), PubSymbol = location.KeySymbol, + MepsLanguageId = location.MepsLanguage, ColorCode = colorCode, TagsCsv = GetTagsAsCsv(tags, note.NoteId, backupFile.Database), }); @@ -63,7 +77,7 @@ internal sealed class NotesExporter notesToWrite.Sort(SortBibleNotes); - return exportService.AppendToBibleNotesFile(bibleNotesExportFilePath, notesToWrite, bibleNotesExportFilePath); + return exportService.Execute(bibleNotesExportFilePath, notesToWrite, bibleNotesExportFilePath); } private static string GetTagsAsCsv(ILookup tags, int noteId, Database database) diff --git a/JWLMerge.ExcelServices/ExcelService.cs b/JWLMerge.ExcelServices/ExcelService.cs index 80aa250..d1f8421 100644 --- a/JWLMerge.ExcelServices/ExcelService.cs +++ b/JWLMerge.ExcelServices/ExcelService.cs @@ -14,28 +14,28 @@ public class ExcelService : IExportToFileService private const string WorkbookName = "Notes"; /// - /// Appends Bible notes to spreadsheet page + /// Exports Bible notes to spreadsheet page /// - /// Excel file path. + /// Excel file path. /// Notes. /// Path to backup file. /// Results. - public ExportBibleNotesResult AppendToBibleNotesFile( - string excelFilePath, + public ExportBibleNotesResult Execute( + string exportFilePath, IReadOnlyCollection? notes, string backupFilePath) { var result = new ExportBibleNotesResult(); var startRow = 0; - if (string.IsNullOrEmpty(excelFilePath)) + if (string.IsNullOrEmpty(exportFilePath)) { - throw new ArgumentNullException(nameof(excelFilePath)); + throw new ArgumentNullException(nameof(exportFilePath)); } - if (!File.Exists(excelFilePath)) + if (!File.Exists(exportFilePath)) { - var lastRow = GenerateHeader(excelFilePath, backupFilePath); + var lastRow = GenerateHeader(exportFilePath, backupFilePath); startRow = lastRow + 2; } @@ -45,7 +45,7 @@ public class ExcelService : IExportToFileService return result; } - using var workbook = new XLWorkbook(excelFilePath); + using var workbook = new XLWorkbook(exportFilePath); if (!workbook.Worksheets.TryGetWorksheet(WorkbookName, out var worksheet)) { @@ -66,16 +66,19 @@ public class ExcelService : IExportToFileService : note.NoteContent; SetCellStringValue(worksheet, row, 1, note.PubSymbol); - SetCellStringValue(worksheet, row, 2, note.BookName); - SetCellIntegerValue(worksheet, row, 3, note.BookNumber); - SetCellIntegerValue(worksheet, row, 4, note.ChapterNumber); - SetCellIntegerValue(worksheet, row, 5, note.VerseNumber); - SetCellStringValue(worksheet, row, 6, note.ChapterAndVerseString); - SetCellStringValue(worksheet, row, 7, note.BookNameChapterAndVerseString); - SetCellIntegerValue(worksheet, row, 8, note.ColorCode); - SetCellStringValue(worksheet, row, 9, note.TagsCsv); - SetCellStringValue(worksheet, row, 10, note.NoteTitle); - SetCellStringValue(worksheet, row, 11, noteContent); + SetCellIntegerValue(worksheet, row, 2, note.MepsLanguageId); + SetCellStringValue(worksheet, row, 3, note.BookName); + SetCellIntegerValue(worksheet, row, 4, note.BookNumber); + SetCellIntegerValue(worksheet, row, 5, note.ChapterNumber); + SetCellIntegerValue(worksheet, row, 6, note.VerseNumber); + SetCellStringValue(worksheet, row, 7, note.ChapterAndVerseString); + SetCellStringValue(worksheet, row, 8, note.BookNameChapterAndVerseString); + SetCellIntegerValue(worksheet, row, 9, note.ColorCode); + SetCellStringValue(worksheet, row, 10, note.TagsCsv); + SetCellIntegerValue(worksheet, row, 11, note.StartTokenInVerse); + SetCellIntegerValue(worksheet, row, 12, note.EndTokenInVerse); + SetCellStringValue(worksheet, row, 13, note.NoteTitle); + SetCellStringValue(worksheet, row, 14, noteContent); ++row; } @@ -106,16 +109,19 @@ public class ExcelService : IExportToFileService worksheet.Cell("A3").Value = $"Exported: {DateTime.Now.ToShortDateString()}"; worksheet.Cell("A5").Value = "Symbol"; - worksheet.Cell("B5").Value = "Book"; - worksheet.Cell("C5").Value = "BookNumber"; - worksheet.Cell("D5").Value = "Chapter"; - worksheet.Cell("E5").Value = "Verse"; - worksheet.Cell("F5").Value = "ChapterAndVerse"; - worksheet.Cell("G5").Value = "FullRef"; - worksheet.Cell("H5").Value = "Color"; - worksheet.Cell("I5").Value = "Tags"; - worksheet.Cell("J5").Value = "Title"; - worksheet.Cell("K5").Value = "Content"; + worksheet.Cell("B5").Value = "LanguageId"; + worksheet.Cell("C5").Value = "Book"; + worksheet.Cell("D5").Value = "BookNumber"; + worksheet.Cell("E5").Value = "Chapter"; + worksheet.Cell("F5").Value = "Verse"; + worksheet.Cell("G5").Value = "ChapterAndVerse"; + worksheet.Cell("H5").Value = "FullRef"; + worksheet.Cell("I5").Value = "Color"; + worksheet.Cell("J5").Value = "Tags"; + worksheet.Cell("K5").Value = "StartToken"; + worksheet.Cell("L5").Value = "EndToken"; + worksheet.Cell("M5").Value = "Title"; + worksheet.Cell("N5").Value = "Content"; workbook.SaveAs(excelFilePath); diff --git a/JWLMerge.ImportExportServices/IExportToFileService.cs b/JWLMerge.ImportExportServices/IExportToFileService.cs index 165a23c..ae36d28 100644 --- a/JWLMerge.ImportExportServices/IExportToFileService.cs +++ b/JWLMerge.ImportExportServices/IExportToFileService.cs @@ -4,8 +4,8 @@ namespace JWLMerge.ImportExportServices; public interface IExportToFileService { - ExportBibleNotesResult AppendToBibleNotesFile( - string excelFilePath, + ExportBibleNotesResult Execute( + string exportFilePath, IReadOnlyCollection? notes, string backupFilePath); } \ No newline at end of file diff --git a/JWLMerge.ImportExportServices/Models/BibleNoteForImportExport.cs b/JWLMerge.ImportExportServices/Models/BibleNoteForImportExport.cs index 8ee2596..7570116 100644 --- a/JWLMerge.ImportExportServices/Models/BibleNoteForImportExport.cs +++ b/JWLMerge.ImportExportServices/Models/BibleNoteForImportExport.cs @@ -16,6 +16,10 @@ public class BibleNoteForImportExport public int? VerseNumber { get; set; } + public int? StartTokenInVerse { get; set; } + + public int? EndTokenInVerse { get; set; } + public string? NoteTitle { get; set; } public string? NoteContent { get; set; } @@ -26,6 +30,8 @@ public class BibleNoteForImportExport public string? PubSymbol { get; set; } + public int? MepsLanguageId { get; set; } + public string ChapterAndVerseString { get diff --git a/JWLMerge.ImportExportServices/TextFileService.cs b/JWLMerge.ImportExportServices/TextFileService.cs index 64056aa..c89f541 100644 --- a/JWLMerge.ImportExportServices/TextFileService.cs +++ b/JWLMerge.ImportExportServices/TextFileService.cs @@ -1,14 +1,90 @@ -using JWLMerge.ImportExportServices.Models; +using System.Text; +using JWLMerge.ImportExportServices.Models; namespace JWLMerge.ImportExportServices; public class TextFileService : IExportToFileService { - public ExportBibleNotesResult AppendToBibleNotesFile( - string excelFilePath, + /// + /// Exports Bible notes to a text file + /// + /// Text file path. + /// Notes. + /// Path to backup file. + /// Results. + public ExportBibleNotesResult Execute( + string exportFilePath, IReadOnlyCollection? notes, string backupFilePath) { - throw new NotImplementedException(); + var result = new ExportBibleNotesResult(); + + if (string.IsNullOrEmpty(exportFilePath)) + { + throw new ArgumentNullException(nameof(exportFilePath)); + } + + if (notes == null || !notes.Any()) + { + result.NoNotesFound = true; + return result; + } + + using var writer = new StreamWriter(exportFilePath); + var section = 0; + + var notesGroupedByPubSymbolAndLanguage = notes.GroupBy(x => (x.PubSymbol, x.MepsLanguageId)); + foreach (var grouping in notesGroupedByPubSymbolAndLanguage) + { + var pubSymbol = grouping.Key.PubSymbol; + var languageId = grouping.Key.MepsLanguageId ?? 0; + + if (grouping.All(x => string.IsNullOrWhiteSpace(x.NoteContent))) + { + continue; + } + + if (section > 0) + { + writer.WriteLine(); + } + + writer.WriteLine($"[BibleKeySymbol={pubSymbol}]"); + writer.WriteLine($"[MepsLanguageId={languageId}]"); + + foreach (var item in grouping) + { + if (string.IsNullOrWhiteSpace(item.NoteContent)) + { + continue; + } + + writer.WriteLine(); + + // [BOOK:CHAP:VS:WORD1:WORD2:COL] + + // BOOK = the 1 - based Bible book index, e.g. 1 = Genesis, 66 = Revelation. + // CHAP = the chapter. + // VS = the verse. + // WORD1 = the 0 - based index of the first token in the verse to which the note applies. + // WORD2 = the 0 - based index of the last token in the verse to which the note applies. + // COL = the colour index to use. + + writer.WriteLine(item is {StartTokenInVerse: 0, EndTokenInVerse: 0, ColorCode: 0} + ? $"[{item.BookNumber}:{item.ChapterNumber ?? 0}:{item.VerseNumber ?? 0}]" + : $"[{item.BookNumber}:{item.ChapterNumber ?? 0}:{item.VerseNumber ?? 0}:{item.StartTokenInVerse ?? 0}:{item.EndTokenInVerse ?? 0}:{item.ColorCode ?? 0}]"); + + if (string.IsNullOrWhiteSpace(item.NoteTitle)) + { + writer.WriteLine(item.NoteTitle); + } + + writer.WriteLine(item.NoteContent); + } + + ++section; + } + + return result; } } \ No newline at end of file diff --git a/JWLMerge/Services/FileOpenSaveService.cs b/JWLMerge/Services/FileOpenSaveService.cs index ac4f00b..ba93146 100644 --- a/JWLMerge/Services/FileOpenSaveService.cs +++ b/JWLMerge/Services/FileOpenSaveService.cs @@ -14,6 +14,8 @@ internal sealed class FileOpenSaveService : IFileOpenSaveService private static string? ExportDirectory { get; set; } + private static int ExportFilterIndex { get; set; } + public string? GetBibleNotesExportFilePath(string title) { var saveFileDialog = new SaveFileDialog @@ -21,12 +23,14 @@ internal sealed class FileOpenSaveService : IFileOpenSaveService AddExtension = true, Title = title, Filter = "Excel file (*.xlsx)|*.xlsx|Text file (*.txt)|*.txt", + FilterIndex = ExportFilterIndex, InitialDirectory = ExportDirectory ?? GetDefaultExportFolder(), }; if (saveFileDialog.ShowDialog() == true) { ExportDirectory = Path.GetDirectoryName(saveFileDialog.FileName); + ExportFilterIndex = saveFileDialog.FilterIndex; return saveFileDialog.FileName; } @@ -94,7 +98,7 @@ internal sealed class FileOpenSaveService : IFileOpenSaveService return null; } - private static string GetJWLMergeDocsFolder() + private static string GetJwlMergeDocsFolder() { var folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "JWLMerge"); if (!Directory.Exists(folder)) @@ -107,12 +111,12 @@ internal sealed class FileOpenSaveService : IFileOpenSaveService private static string GetDefaultSaveFolder() { - return GetJWLMergeDocsFolder(); + return GetJwlMergeDocsFolder(); } private static string GetDefaultExportFolder() { - return GetJWLMergeDocsFolder(); + return GetJwlMergeDocsFolder(); } private static string GetDefaultImportFolder()