From f67f23d9da1150d22d6994bd9a4aa85d08cbeaf1 Mon Sep 17 00:00:00 2001 From: AntonyCorbett Date: Fri, 10 Apr 2020 16:40:00 +0100 Subject: [PATCH] First attempt at db v7 --- .../BackupFileService.cs | 2 +- .../Helpers/Cleaner.cs | 4 +- .../Helpers/DataAccessLayer.cs | 14 +- JWLMerge.BackupFileServices/Helpers/Merger.cs | 85 ++++---- .../Helpers/NotesImporter.cs | 3 +- .../JWLMerge.BackupFileServices.csproj | 1 + .../Models/Database/Database.cs | 70 +++++-- .../Models/Database/Tag.cs | 8 +- .../Models/Database/TagMap.cs | 24 ++- .../Models/TagTypeAndName.cs | 35 ++++ JWLMerge/JWLMerge.ruleset | 3 +- SampleBibleNotes/Test.txt | 17 -- Version005.txt | 97 +++++---- Version007.txt | 195 ++++++++++++++++++ 14 files changed, 432 insertions(+), 126 deletions(-) create mode 100644 JWLMerge.BackupFileServices/Models/TagTypeAndName.cs delete mode 100644 SampleBibleNotes/Test.txt create mode 100644 Version007.txt diff --git a/JWLMerge.BackupFileServices/BackupFileService.cs b/JWLMerge.BackupFileServices/BackupFileService.cs index ab07b15..0bf2448 100644 --- a/JWLMerge.BackupFileServices/BackupFileService.cs +++ b/JWLMerge.BackupFileServices/BackupFileService.cs @@ -21,7 +21,7 @@ public sealed class BackupFileService : IBackupFileService { private const int ManifestVersionSupported = 1; - private const int DatabaseVersionSupported = 5; + private const int DatabaseVersionSupported = 7; private const string ManifestEntryName = "manifest.json"; private const string DatabaseEntryName = "userData.db"; diff --git a/JWLMerge.BackupFileServices/Helpers/Cleaner.cs b/JWLMerge.BackupFileServices/Helpers/Cleaner.cs index 2090f3e..73a52d4 100644 --- a/JWLMerge.BackupFileServices/Helpers/Cleaner.cs +++ b/JWLMerge.BackupFileServices/Helpers/Cleaner.cs @@ -63,9 +63,9 @@ foreach (var tagMap in _database.TagMaps) { - if (tagMap.Type == 0) + if (tagMap.LocationId != null) { - result.Add(tagMap.TypeId); + result.Add(tagMap.LocationId.Value); } } diff --git a/JWLMerge.BackupFileServices/Helpers/DataAccessLayer.cs b/JWLMerge.BackupFileServices/Helpers/DataAccessLayer.cs index 9241d08..30397bd 100644 --- a/JWLMerge.BackupFileServices/Helpers/DataAccessLayer.cs +++ b/JWLMerge.BackupFileServices/Helpers/DataAccessLayer.cs @@ -170,6 +170,7 @@ TagId = ReadInt(reader, "TagId"), Type = ReadInt(reader, "Type"), Name = ReadString(reader, "Name"), + ImageFileName = ReadNullableString(reader, "ImageFilename"), // added in db v7 April 2020 }; } @@ -178,8 +179,17 @@ return new TagMap { TagMapId = ReadInt(reader, "TagMapId"), - Type = ReadInt(reader, "Type"), - TypeId = ReadInt(reader, "TypeId"), + + // removed in db v7, April 2020 + //Type = ReadInt(reader, "Type"), + //TypeId = ReadInt(reader, "TypeId"), + + // added in db v7, April 2020... + PlaylistItemId = ReadNullableInt(reader, "PlaylistItemId"), + LocationId = ReadNullableInt(reader, "LocationId"), + NoteId = ReadNullableInt(reader, "NoteId"), + //... + TagId = ReadInt(reader, "TagId"), Position = ReadInt(reader, "Position"), }; diff --git a/JWLMerge.BackupFileServices/Helpers/Merger.cs b/JWLMerge.BackupFileServices/Helpers/Merger.cs index 01bbbf0..f15acbb 100644 --- a/JWLMerge.BackupFileServices/Helpers/Merger.cs +++ b/JWLMerge.BackupFileServices/Helpers/Merger.cs @@ -183,37 +183,40 @@ foreach (var tagMap in source.TagMaps) { - var tagId = _translatedTagIds.GetTranslatedId(tagMap.TagId); - var typeId = 0; - - switch (tagMap.Type) + if (tagMap.PlaylistItemId != null) { - case 0: - // a tag on a location - typeId = _translatedLocationIds.GetTranslatedId(tagMap.TypeId); - if (typeId == 0) - { - // must add location... - var location = source.FindLocation(tagMap.TypeId); - InsertLocation(location, destination); - typeId = _translatedLocationIds.GetTranslatedId(tagMap.TypeId); - } - - break; - - case 1: - // a tag on a Note - typeId = _translatedNoteIds.GetTranslatedId(tagMap.TypeId); - break; + // we ignore playlist during merge + continue; } - if (typeId != 0) + var tagId = _translatedTagIds.GetTranslatedId(tagMap.TagId); + var id = 0; + TagMap existingTagMap = null; + + if (tagMap.LocationId != null) { - var existingTagMap = destination.FindTagMap(tagId, typeId); - if (existingTagMap == null) + // a tag on a location. + id = _translatedLocationIds.GetTranslatedId(tagMap.LocationId.Value); + if (id == 0) { - InsertTagMap(tagMap, destination); + // must add location... + var location = source.FindLocation(tagMap.LocationId.Value); + InsertLocation(location, destination); + id = _translatedLocationIds.GetTranslatedId(tagMap.LocationId.Value); } + + existingTagMap = destination.FindTagMapForLocation(tagId, id); + } + else if (tagMap.NoteId != null) + { + // a tag on a Note + id = _translatedNoteIds.GetTranslatedId(tagMap.NoteId.Value); + existingTagMap = destination.FindTagMapForNote(tagId, id); + } + + if (id != 0 && existingTagMap == null) + { + InsertTagMap(tagMap, destination); } } } @@ -224,7 +227,7 @@ foreach (var tag in source.Tags) { - var existingTag = destination.FindTag(tag.Name); + var existingTag = destination.FindTag(tag.Type, tag.Name); if (existingTag != null) { _translatedTagIds.Add(tag.TagId, existingTag.TagId); @@ -300,24 +303,30 @@ private void InsertTagMap(TagMap tagMap, Database destination) { + if (tagMap.PlaylistItemId != null) + { + // we ignore playlists during merge. + return; + } + TagMap newTagMap = tagMap.Clone(); newTagMap.TagMapId = ++_maxTagMapId; newTagMap.TagId = _translatedTagIds.GetTranslatedId(tagMap.TagId); - newTagMap.TypeId = 0; - - switch (newTagMap.Type) - { - case 0: - newTagMap.TypeId = _translatedLocationIds.GetTranslatedId(tagMap.TypeId); - break; + newTagMap.LocationId = null; + newTagMap.PlaylistItemId = null; + newTagMap.NoteId = null; - case 1: - newTagMap.TypeId = _translatedNoteIds.GetTranslatedId(tagMap.TypeId); - break; + if (tagMap.LocationId != null) + { + newTagMap.LocationId = _translatedLocationIds.GetTranslatedId(tagMap.LocationId.Value); } - - if (newTagMap.TypeId != 0) + else if (tagMap.NoteId != null) + { + newTagMap.NoteId = _translatedNoteIds.GetTranslatedId(tagMap.NoteId.Value); + } + + if (newTagMap.LocationId != null || newTagMap.NoteId != null) { destination.TagMaps.Add(newTagMap); } diff --git a/JWLMerge.BackupFileServices/Helpers/NotesImporter.cs b/JWLMerge.BackupFileServices/Helpers/NotesImporter.cs index 3f94b85..92f032c 100644 --- a/JWLMerge.BackupFileServices/Helpers/NotesImporter.cs +++ b/JWLMerge.BackupFileServices/Helpers/NotesImporter.cs @@ -136,8 +136,7 @@ { TagMapId = ++_maxTagMapId, TagId = tagId, - Type = 1, // tag is on a note - TypeId = noteId, + NoteId = noteId, }; } diff --git a/JWLMerge.BackupFileServices/JWLMerge.BackupFileServices.csproj b/JWLMerge.BackupFileServices/JWLMerge.BackupFileServices.csproj index 1425287..b529c01 100644 --- a/JWLMerge.BackupFileServices/JWLMerge.BackupFileServices.csproj +++ b/JWLMerge.BackupFileServices/JWLMerge.BackupFileServices.csproj @@ -94,6 +94,7 @@ + diff --git a/JWLMerge.BackupFileServices/Models/Database/Database.cs b/JWLMerge.BackupFileServices/Models/Database/Database.cs index 89665f5..ba23c2f 100644 --- a/JWLMerge.BackupFileServices/Models/Database/Database.cs +++ b/JWLMerge.BackupFileServices/Models/Database/Database.cs @@ -1,10 +1,10 @@ namespace JWLMerge.BackupFileServices.Models.Database { - using Serilog; using System; using System.Collections.Generic; using System.Linq; using JWLMerge.BackupFileServices.Exceptions; + using Serilog; public class Database { @@ -19,9 +19,10 @@ private Lazy> _locationsIdIndex; private Lazy> _locationsValueIndex; private Lazy> _locationsBibleChapterIndex; - private Lazy> _tagsNameIndex; + private Lazy> _tagsNameIndex; private Lazy> _tagsIdIndex; - private Lazy> _tagMapIndex; + private Lazy> _tagMapNoteIndex; + private Lazy> _tagMapLocationIndex; private Lazy>> _blockRangesUserMarkIdIndex; private Lazy> _bookmarksIndex; @@ -232,9 +233,10 @@ return _userMarksLocationIdIndex.Value.TryGetValue(locationId, out var userMarks) ? userMarks : null; } - public Tag FindTag(string tagName) + public Tag FindTag(int tagType, string tagName) { - return _tagsNameIndex.Value.TryGetValue(tagName, out var tag) ? tag : null; + var key = new TagTypeAndName(tagType, tagName); + return _tagsNameIndex.Value.TryGetValue(key, out var tag) ? tag : null; } public Tag FindTag(int tagId) @@ -242,9 +244,14 @@ return _tagsIdIndex.Value.TryGetValue(tagId, out var tag) ? tag : null; } - public TagMap FindTagMap(int tagId, int noteId) + public TagMap FindTagMapForNote(int tagId, int noteId) { - return _tagMapIndex.Value.TryGetValue(GetTagMapKey(tagId, noteId), out var tag) ? tag : null; + return _tagMapNoteIndex.Value.TryGetValue(GetTagMapNoteKey(tagId, noteId), out var tag) ? tag : null; + } + + public TagMap FindTagMapForLocation(int tagId, int locationId) + { + return _tagMapLocationIndex.Value.TryGetValue(GetTagMapLocationKey(tagId, locationId), out var tag) ? tag : null; } public Location FindLocation(int locationId) @@ -453,9 +460,9 @@ return result; } - private Dictionary TagIndexValueFactory() + private Dictionary TagIndexValueFactory() { - return Tags.ToDictionary(tag => tag.Name); + return Tags.ToDictionary(tag => new TagTypeAndName(tag.Type, tag.Name)); } private Dictionary TagIdIndexValueFactory() @@ -463,19 +470,43 @@ return Tags.ToDictionary(tag => tag.TagId); } - private string GetTagMapKey(int tagId, int noteId) + private string GetTagMapNoteKey(int tagId, int noteId) { return $"{tagId}-{noteId}"; } - private Dictionary TagMapIndexValueFactory() + private string GetTagMapLocationKey(int tagId, int locationId) + { + return $"{tagId}-{locationId}"; + } + + private Dictionary TagMapNoteIndexValueFactory() { var result = new Dictionary(); foreach (var tagMap in TagMaps) { - string key = GetTagMapKey(tagMap.TagId, tagMap.TypeId); - result.Add(key, tagMap); + if (tagMap.NoteId != null) + { + string key = GetTagMapNoteKey(tagMap.TagId, tagMap.NoteId.Value); + result.Add(key, tagMap); + } + } + + return result; + } + + private Dictionary TagMapLocationIndexValueFactory() + { + var result = new Dictionary(); + + foreach (var tagMap in TagMaps) + { + if (tagMap.LocationId != null) + { + string key = GetTagMapLocationKey(tagMap.TagId, tagMap.LocationId.Value); + result.Add(key, tagMap); + } } return result; @@ -496,14 +527,14 @@ { foreach (var tagMap in TagMaps) { - if (tagMap.Type == 1) + if (tagMap.NoteId != null) { if (FindTag(tagMap.TagId) == null) { throw new BackupFileServicesException($"Could not find tag for tag map {tagMap.TagMapId}"); } - if (FindNote(tagMap.TypeId) == null) + if (FindNote(tagMap.NoteId.Value) == null) { throw new BackupFileServicesException($"Could not find note for tag map {tagMap.TagMapId}"); } @@ -561,9 +592,10 @@ _locationsIdIndex = new Lazy>(LocationsIndexValueFactory); _locationsValueIndex = new Lazy>(LocationsByValueIndexValueFactory); _locationsBibleChapterIndex = new Lazy>(LocationsByBibleChapterIndexValueFactory); - _tagsNameIndex = new Lazy>(TagIndexValueFactory); + _tagsNameIndex = new Lazy>(TagIndexValueFactory); _tagsIdIndex = new Lazy>(TagIdIndexValueFactory); - _tagMapIndex = new Lazy>(TagMapIndexValueFactory); + _tagMapNoteIndex = new Lazy>(TagMapNoteIndexValueFactory); + _tagMapLocationIndex = new Lazy>(TagMapLocationIndexValueFactory); _blockRangesUserMarkIdIndex = new Lazy>>(BlockRangeIndexValueFactory); _bookmarksIndex = new Lazy>(BookmarkIndexValueFactory); } @@ -596,8 +628,8 @@ { var tagMap = TagMaps[n]; - if (tagMap.Type == 1 && - (FindTag(tagMap.TagId) == null || FindNote(tagMap.TypeId) == null)) + if (tagMap.NoteId != null && + (FindTag(tagMap.TagId) == null || FindNote(tagMap.NoteId.Value) == null)) { ++fixupCount; TagMaps.RemoveAt(n); diff --git a/JWLMerge.BackupFileServices/Models/Database/Tag.cs b/JWLMerge.BackupFileServices/Models/Database/Tag.cs index 9de03dc..0054fca 100644 --- a/JWLMerge.BackupFileServices/Models/Database/Tag.cs +++ b/JWLMerge.BackupFileServices/Models/Database/Tag.cs @@ -9,7 +9,7 @@ /// /// The tag type. - /// There appear to be 2 tag types (0 = Favourite, 1 = User-defined). + /// There appear to be 3 tag types (0 = Favourite, 1 = User-defined, 2 = ?). /// public int Type { get; set; } @@ -18,6 +18,12 @@ /// public string Name { get; set; } + /// + /// The optional image file name. + /// + /// Added in db ver 7 April 2020. + public string ImageFileName { get; set; } + public Tag Clone() { return (Tag)MemberwiseClone(); diff --git a/JWLMerge.BackupFileServices/Models/Database/TagMap.cs b/JWLMerge.BackupFileServices/Models/Database/TagMap.cs index bc0fb85..ad79fca 100644 --- a/JWLMerge.BackupFileServices/Models/Database/TagMap.cs +++ b/JWLMerge.BackupFileServices/Models/Database/TagMap.cs @@ -6,19 +6,39 @@ /// The tag map identifier. /// public int TagMapId { get; set; } + + /// + /// Playlist Item Id. + /// + /// added in in db v7, Apr 2020 + public int? PlaylistItemId { get; set; } + /// + /// Location Id. + /// + /// added in in db v7, Apr 2020 + public int? LocationId { get; set; } + + /// + /// Note Id. + /// + /// added in in db v7, Apr 2020 + public int? NoteId { get; set; } + + // removed in db v7 /// /// The type of data that the tag is attached to. /// 0 = tag on a Location /// 1 = tag on a Note /// - public int Type { get; set; } + //public int Type { get; set; } + // removed in db v7 /// /// The identifier of the data that the tag is attached to. /// Currently it looks like this always refers to Note.NoteId /// - public int TypeId { get; set; } + //public int TypeId { get; set; } /// /// The tag identifier. diff --git a/JWLMerge.BackupFileServices/Models/TagTypeAndName.cs b/JWLMerge.BackupFileServices/Models/TagTypeAndName.cs new file mode 100644 index 0000000..bb760b1 --- /dev/null +++ b/JWLMerge.BackupFileServices/Models/TagTypeAndName.cs @@ -0,0 +1,35 @@ +namespace JWLMerge.BackupFileServices.Models +{ + internal class TagTypeAndName + { + public TagTypeAndName(int type, string name) + { + TagType = type; + Name = name; + } + + public int TagType { get; } + + public string Name { get; } + + public override int GetHashCode() + { + return new { TagType, Name }.GetHashCode(); + } + + public override bool Equals(object obj) + { + switch (obj) + { + case null: + return false; + + case TagTypeAndName o: + return TagType == o.TagType && + Name == o.Name; + default: + return false; + } + } + } +} diff --git a/JWLMerge/JWLMerge.ruleset b/JWLMerge/JWLMerge.ruleset index 0ed5e78..9252e87 100644 --- a/JWLMerge/JWLMerge.ruleset +++ b/JWLMerge/JWLMerge.ruleset @@ -1,5 +1,5 @@  - + @@ -72,6 +72,7 @@ + diff --git a/SampleBibleNotes/Test.txt b/SampleBibleNotes/Test.txt deleted file mode 100644 index 37e2b38..0000000 --- a/SampleBibleNotes/Test.txt +++ /dev/null @@ -1,17 +0,0 @@ -[BibleKeySymbol=nwtsty] -[MepsLanguageId = 0] - -[1:1:1] -JFB - -Ge 1:1, 2. The Creation of Heaven and Earth.1. In the beginning—a period of remote and unknown antiquity, hid in the depths of eternal ages; and so the phrase is used in Pr 8:22, 23.God—the name of the Supreme Being, signifying in Hebrew, "Strong," "Mighty." It is expressive of omnipotent power; and by its use here in the plural form, is obscurely taught at the opening of the Bible, a doctrine clearly revealed in other parts of it, namely, that though God is one, there is a plurality of persons in the Godhead—Father, Son, and Spirit, who were engaged in the creative work (Pr 8:27; Joh 1:3, 10; Eph 3:9; Heb 1:2; Job 26:13).created—not formed from any pre-existing materials, but made out of nothing.the heaven and the earth—the universe. This first verse is a general introduction to the inspired volume, declaring the great and important truth that all things had a beginning; that nothing throughout the wide extent of nature existed from eternity, originated by chance, or from the skill of any inferior agent; but that the whole universe was produced by the creative power of God (Ac 17:24; Ro 11:36). After this preface, the narrative is confined to the earth. - -[1:1:2] -JFB - -2. the earth was without form and void—or in "confusion and emptiness," as the words are rendered in Isa 34:11. This globe, at some undescribed period, having been convulsed and broken up, was a dark and watery waste for ages perhaps, till out of this chaotic state, the present fabric of the world was made to arise.the Spirit of God moved—literally, continued brooding over it, as a fowl does, when hatching eggs. The immediate agency of the Spirit, by working on the dead and discordant elements, combined, arranged, and ripened them into a state adapted for being the scene of a new creation. The account of this new creation properly begins at the end of this second verse; and the details of the process are described in the natural way an onlooker would have done, who beheld the changes that successively took place. - -[1:1:3] -JFB - -Ge 1:3-5. The First Day.3. God said—This phrase, which occurs so repeatedly in the account means: willed, decreed, appointed; and the determining will of God was followed in every instance by an immediate result. Whether the sun was created at the same time with, or long before, the earth, the dense accumulation of fogs and vapors which enveloped the chaos had covered the globe with a settled gloom. But by the command of God, light was rendered visible; the thick murky clouds were dispersed, broken, or rarefied, and light diffused over the expanse of waters. The effect is described in the name "day," which in Hebrew signifies "warmth," "heat"; while the name "night" signifies a "rolling up," as night wraps all things in a shady mantle. diff --git a/Version005.txt b/Version005.txt index 3bcf1ac..d938dab 100644 --- a/Version005.txt +++ b/Version005.txt @@ -1,30 +1,4 @@ -CREATE TABLE BlockRange ( - BlockRangeId INTEGER NOT NULL PRIMARY KEY, - BlockType INTEGER NOT NULL, - Identifier INTEGER NOT NULL, - StartToken INTEGER, - EndToken INTEGER, - UserMarkId INTEGER NOT NULL, - CHECK (BlockType BETWEEN 1 AND 2), - FOREIGN KEY(UserMarkId) REFERENCES UserMark(UserMarkId) - ); - -CREATE TABLE Bookmark( - BookmarkId INTEGER NOT NULL PRIMARY KEY, - LocationId INTEGER NOT NULL, - PublicationLocationId INTEGER NOT NULL, - Slot INTEGER NOT NULL, - Title TEXT NOT NULL, - Snippet TEXT, - BlockType INTEGER NOT NULL DEFAULT 0, - BlockIdentifier INTEGER, - FOREIGN KEY(LocationId) REFERENCES Location(LocationId), - FOREIGN KEY(PublicationLocationId) REFERENCES Location(LocationId), - CONSTRAINT PublicationLocationId_Slot UNIQUE (PublicationLocationId, Slot)); - -CREATE TABLE LastModified(LastModified TEXT NOT NULL DEFAULT(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))); - -CREATE TABLE Location ( +CREATE TABLE "Location" ( LocationId INTEGER NOT NULL PRIMARY KEY, BookNumber INTEGER, ChapterNumber INTEGER, @@ -43,20 +17,7 @@ CREATE TABLE Location ( ) ); -CREATE TABLE Note( NoteId INTEGER NOT NULL PRIMARY KEY, Guid TEXT NOT NULL UNIQUE, UserMarkId INTEGER, LocationId INTEGER, Title TEXT, Content TEXT, LastModified TEXT NOT NULL DEFAULT(strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), BlockType INTEGER NOT NULL DEFAULT 0, BlockIdentifier INTEGER, CHECK ((BlockType = 0 AND BlockIdentifier IS NULL) OR (BlockType != 0 AND BlockIdentifier IS NOT NULL))); - -CREATE TABLE Tag(TagId INTEGER NOT NULL PRIMARY KEY, Type INTEGER NOT NULL,Name TEXT NOT NULL,UNIQUE (Type, Name), CHECK (length(Name) > 0)); - -CREATE TABLE TagMap ( - TagMapId INTEGER NOT NULL PRIMARY KEY, - Type INTEGER NOT NULL, - TypeId INTEGER NOT NULL, - TagId INTEGER NOT NULL, - Position INTEGER NOT NULL, - FOREIGN KEY(TagId) REFERENCES Tag(TagId) - CONSTRAINT Type_TypeId_TagId_Position UNIQUE (Type, TypeId, TagId, Position)); - -CREATE TABLE UserMark ( +CREATE TABLE "UserMark" ( UserMarkId INTEGER NOT NULL PRIMARY KEY, ColorIndex INTEGER NOT NULL, LocationId INTEGER NOT NULL, @@ -67,6 +28,60 @@ CREATE TABLE UserMark ( FOREIGN KEY(LocationId) REFERENCES Location(LocationId) ); +CREATE TABLE BlockRange ( + BlockRangeId INTEGER NOT NULL PRIMARY KEY, + BlockType INTEGER NOT NULL, + Identifier INTEGER NOT NULL, + StartToken INTEGER, + EndToken INTEGER, + UserMarkId INTEGER NOT NULL, + CHECK (BlockType BETWEEN 1 AND 2), + FOREIGN KEY(UserMarkId) REFERENCES UserMark(UserMarkId) + ); + +CREATE TABLE "Bookmark" ( + BookmarkId INTEGER NOT NULL PRIMARY KEY, + LocationId INTEGER NOT NULL, + PublicationLocationId INTEGER NOT NULL, + Slot INTEGER NOT NULL, + Title TEXT NOT NULL, + Snippet TEXT, + BlockType INTEGER NOT NULL DEFAULT 0, + BlockIdentifier INTEGER, + FOREIGN KEY(LocationId) REFERENCES Location(LocationId), + FOREIGN KEY(PublicationLocationId) REFERENCES Location(LocationId), + CONSTRAINT PublicationLocationId_Slot UNIQUE (PublicationLocationId, Slot)); + +CREATE TABLE LastModified(LastModified TEXT NOT NULL DEFAULT(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))); + +CREATE TABLE "Note" ( +NoteId INTEGER NOT NULL PRIMARY KEY, +Guid TEXT NOT NULL UNIQUE, +UserMarkId INTEGER, +LocationId INTEGER, +Title TEXT, +Content TEXT, +LastModified TEXT NOT NULL DEFAULT(strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), +BlockType INTEGER NOT NULL DEFAULT 0, +BlockIdentifier INTEGER, +CHECK ((BlockType = 0 AND BlockIdentifier IS NULL) OR (BlockType != 0 AND BlockIdentifier IS NOT NULL))); + +CREATE TABLE "Tag" ( +TagId INTEGER NOT NULL PRIMARY KEY, +Type INTEGER NOT NULL, +Name TEXT NOT NULL, +UNIQUE (Type, Name), +CHECK (length(Name) > 0)); + +CREATE TABLE "TagMap" ( + TagMapId INTEGER NOT NULL PRIMARY KEY, + Type INTEGER NOT NULL, + TypeId INTEGER NOT NULL, + TagId INTEGER NOT NULL, + Position INTEGER NOT NULL, + FOREIGN KEY(TagId) REFERENCES Tag(TagId) + CONSTRAINT Type_TypeId_TagId_Position UNIQUE (Type, TypeId, TagId, Position)); + CREATE INDEX IX_BlockRange_UserMarkId ON BlockRange(UserMarkId); CREATE INDEX IX_Location_KeySymbol_MepsLanguage_BookNumber_ChapterNumber ON diff --git a/Version007.txt b/Version007.txt new file mode 100644 index 0000000..01f9ede --- /dev/null +++ b/Version007.txt @@ -0,0 +1,195 @@ +Version 7 followed on from Version 5. Changes (as they affect JWLMerge) are as follows: + +"Tag" Table +Added column "ImageFilename" (so we can tag images?) + +"TagMap" Table +Removed colum "Type" +Removed colum "TypeId" +Added column "PlayListItemId" +Added column "LocationItemId" +Added column "NoteId" + +One of the above new columns is non-null + +Here's the v7 schema: + +CREATE TABLE "Location" ( + LocationId INTEGER NOT NULL PRIMARY KEY, + BookNumber INTEGER, + ChapterNumber INTEGER, + DocumentId INTEGER, + Track INTEGER, + IssueTagNumber INTEGER NOT NULL DEFAULT 0, + KeySymbol TEXT NOT NULL, + MepsLanguage INTEGER NOT NULL, + Type INTEGER NOT NULL, + Title TEXT, + UNIQUE(BookNumber, ChapterNumber, KeySymbol, MepsLanguage, Type), + UNIQUE(KeySymbol, IssueTagNumber, MepsLanguage, DocumentId, Track, Type), + CHECK ( + (Type = 0 AND (DocumentId IS NOT NULL AND DocumentId != 0) AND BookNumber IS NULL AND ChapterNumber IS NULL AND Track IS NULL) OR + (Type = 0 AND DocumentId IS NULL AND (BookNumber IS NOT NULL AND BookNumber != 0) AND (ChapterNumber IS NOT NULL AND ChapterNumber != 0) AND Track IS NULL) OR + (Type = 1 AND BookNumber IS NULL AND ChapterNumber IS NULL AND DocumentId IS NULL AND Track IS NULL) OR + (Type IN (2, 3) AND BookNumber IS NULL AND ChapterNumber IS NULL) + ) + ); + +CREATE TABLE "UserMark" ( + UserMarkId INTEGER NOT NULL PRIMARY KEY, + ColorIndex INTEGER NOT NULL, + LocationId INTEGER NOT NULL, + StyleIndex INTEGER NOT NULL, + UserMarkGuid TEXT NOT NULL UNIQUE, + Version INTEGER NOT NULL, + FOREIGN KEY(LocationId) REFERENCES Location(LocationId) + ); + +CREATE TABLE BlockRange ( + BlockRangeId INTEGER NOT NULL PRIMARY KEY, + BlockType INTEGER NOT NULL, + Identifier INTEGER NOT NULL, + StartToken INTEGER, + EndToken INTEGER, + UserMarkId INTEGER NOT NULL, + CHECK (BlockType BETWEEN 1 AND 2), + FOREIGN KEY(UserMarkId) REFERENCES UserMark(UserMarkId) + ); + +CREATE TABLE "Bookmark" ( + BookmarkId INTEGER NOT NULL PRIMARY KEY, + LocationId INTEGER NOT NULL, + PublicationLocationId INTEGER NOT NULL, + Slot INTEGER NOT NULL, + Title TEXT NOT NULL, + Snippet TEXT, + BlockType INTEGER NOT NULL DEFAULT 0, + BlockIdentifier INTEGER, + FOREIGN KEY(LocationId) REFERENCES Location(LocationId), + FOREIGN KEY(PublicationLocationId) REFERENCES Location(LocationId), + CONSTRAINT PublicationLocationId_Slot UNIQUE (PublicationLocationId, Slot), + CHECK((BlockType = 0 AND BlockIdentifier IS NULL) OR ((BlockType BETWEEN 1 AND 2) AND BlockIdentifier IS NOT NULL)) + ); + +CREATE TABLE LastModified(LastModified TEXT NOT NULL DEFAULT(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))); + +CREATE TABLE "Note" ( + NoteId INTEGER NOT NULL PRIMARY KEY, + Guid TEXT NOT NULL UNIQUE, + UserMarkId INTEGER, + LocationId INTEGER, + Title TEXT, + Content TEXT, + LastModified TEXT NOT NULL DEFAULT(strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + BlockType INTEGER NOT NULL DEFAULT 0, + BlockIdentifier INTEGER, + CHECK((BlockType = 0 AND BlockIdentifier IS NULL) OR ((BlockType BETWEEN 1 AND 2) AND BlockIdentifier IS NOT NULL)), + FOREIGN KEY(UserMarkId) REFERENCES UserMark(UserMarkId), + FOREIGN KEY(LocationId) REFERENCES Location(LocationId) + ); + +CREATE TABLE PlaylistMedia( + PlaylistMediaId INTEGER NOT NULL PRIMARY KEY, + MediaType INTEGER NOT NULL, + Label TEXT, + Filename TEXT UNIQUE, + LocationId INTEGER, + FOREIGN KEY(LocationId) REFERENCES Location(LocationId), + CONSTRAINT MediaType_LocationId UNIQUE(MediaType, LocationId), + CHECK( + (LocationId IS NULL AND Filename IS NOT NULL AND Label IS NOT NULL) OR + (LocationId IS NOT NULL AND Filename IS NULL AND Label IS NULL) OR + (LocationId IS NOT NULL AND Filename IS NOT NULL)), + CHECK(MediaType IN(1, 2, 3)) + ); + +CREATE TABLE PlaylistItem( + PlaylistItemId INTEGER NOT NULL PRIMARY KEY, + Label TEXT NOT NULL, + AccuracyStatement INTEGER NOT NULL, + StartTimeOffsetTicks INTEGER, + EndTimeOffsetTicks INTEGER, + EndAction INTEGER NOT NULL, + ThumbnailFilename TEXT, + PlaylistMediaId INTEGER NOT NULL, + FOREIGN KEY(PlaylistMediaId) REFERENCES PlaylistMedia(PlaylistMediaId), + CHECK(length(Label) > 0), + CHECK(AccuracyStatement IN(0, 1, 2, 3)), + CHECK(EndAction IN(0, 1, 2, 3)) + ); + +CREATE TABLE PlaylistItemChild( + PlaylistItemChildId INTEGER NOT NULL PRIMARY KEY, + BaseDurationTicks INTEGER NOT NULL, + MarkerId INTEGER, + MarkerLabel TEXT, + MarkerStartTimeTicks INTEGER, + MarkerEndTransitionDurationTicks INTEGER, + PlaylistItemId INTEGER NOT NULL, + FOREIGN KEY(PlaylistItemId) REFERENCES PlaylistItem(PlaylistItemId), + CHECK( + (MarkerId IS NULL AND MarkerLabel IS NULL AND MarkerStartTimeTicks IS NULL AND MarkerEndTransitionDurationTicks IS NULL) OR + (MarkerId IS NOT NULL AND MarkerLabel IS NOT NULL AND MarkerStartTimeTicks IS NOT NULL) + ) + ); + +CREATE TABLE "Tag" ( + TagId INTEGER NOT NULL PRIMARY KEY, + Type INTEGER NOT NULL, + Name TEXT NOT NULL, + ImageFilename TEXT, + UNIQUE(Type, Name), + CHECK(length(Name) > 0), + CHECK(Type IN (0, 1, 2)) + ); + +CREATE TABLE "TagMap" ( + TagMapId INTEGER NOT NULL PRIMARY KEY, + PlaylistItemId INTEGER, + LocationId INTEGER, + NoteId INTEGER, + TagId INTEGER NOT NULL, + Position INTEGER NOT NULL, + FOREIGN KEY(TagId) REFERENCES Tag(TagId), + FOREIGN KEY(PlaylistItemId) REFERENCES PlaylistItem(PlaylistItemId), + FOREIGN KEY(LocationId) REFERENCES Location(LocationId), + FOREIGN KEY(NoteId) REFERENCES Note(NoteId), + CONSTRAINT TagId_Position UNIQUE(TagId, Position), + CONSTRAINT TagId_NoteId UNIQUE(TagId, NoteId), + CONSTRAINT TagId_LocationId UNIQUE(TagId, LocationId), + CHECK( + (NoteId IS NULL AND LocationId IS NULL AND PlaylistItemId IS NOT NULL) OR + (LocationId IS NULL AND PlaylistItemId IS NULL AND NoteId IS NOT NULL) OR + (PlaylistItemId IS NULL AND NoteId IS NULL AND LocationId IS NOT NULL)) + ); + +CREATE VIEW PlaylistView AS + SELECT t.Name, t.ImageFilename, Count(tm.TagId) AS ItemCount + FROM Tag t LEFT JOIN TagMap tm ON tm.TagId=t.TagId + WHERE t.Type=2 + GROUP BY t.TagId + ORDER BY t.Name COLLATE NOCASE; + +CREATE INDEX IX_BlockRange_UserMarkId ON BlockRange(UserMarkId); + +CREATE INDEX IX_Location_KeySymbol_MepsLanguage_BookNumber_ChapterNumber ON + Location(KeySymbol, MepsLanguage, BookNumber, ChapterNumber); + +CREATE INDEX IX_Location_MepsLanguage_DocumentId ON Location(MepsLanguage, DocumentId); + +CREATE INDEX IX_Note_LastModified_LocationId ON Note(LastModified, LocationId); + +CREATE INDEX IX_Note_LocationId_BlockIdentifier ON Note(LocationId, BlockIdentifier); + +CREATE INDEX IX_TagMap_LocationId_TagId_Position ON TagMap(LocationId, TagId, Position); + +CREATE INDEX IX_TagMap_NoteId_TagId_Position ON TagMap(NoteId, TagId, Position); + +CREATE INDEX IX_TagMap_PlaylistItemId_TagId_Position ON TagMap(PlaylistItemId, TagId, Position); + +CREATE INDEX IX_TagMap_TagId ON TagMap(TagId); + +CREATE INDEX IX_Tag_Name_Type_TagId ON Tag(Name, Type, TagId); + +CREATE INDEX IX_UserMark_LocationId ON UserMark(LocationId); +