From 367949c200712deceb5ecd7cc9adf26b11d3963c Mon Sep 17 00:00:00 2001 From: Max <51083570+DRdrProfessor@users.noreply.github.com> Date: Sun, 21 May 2023 21:56:37 +0200 Subject: [PATCH] Implementing database backend. --- SharpRss/Models/FeedItemModel.cs | 17 +-- SharpRss/Models/FeedModel.cs | 26 +++-- SharpRss/Models/GroupModel.cs | 10 +- SharpRss/Services/DatabaseService.cs | 153 ++++++++++++++++++--------- SharpRss/Services/RssService.cs | 70 +++--------- SharpRss/SharpRss.csproj | 1 + WebSharpRSS/sharp_rss.sqlite | Bin 36864 -> 28672 bytes 7 files changed, 141 insertions(+), 136 deletions(-) diff --git a/SharpRss/Models/FeedItemModel.cs b/SharpRss/Models/FeedItemModel.cs index 5a812fc..b153340 100644 --- a/SharpRss/Models/FeedItemModel.cs +++ b/SharpRss/Models/FeedItemModel.cs @@ -1,30 +1,19 @@ using System; -using System.Collections.Generic; -using System.Text; namespace SharpRss.Models { public class FeedItemModel { - /// - /// Last time the item is fetched. - /// - public DateTime LastUpdated { get; set; } - /// - /// The feed in which the item is part of. - /// + public string Id { get; set; } public string FeedId { get; set; } - /// - /// If the item is read. - /// public bool Read { get; set; } public string Type { get; set; } public string Title { get; set; } public string Description { get; set; } public string Link { get; set; } - public DateTime? PublishingDate { get; set; } + public DateTimeOffset LastUpdated { get; set; } + public DateTimeOffset? PublishingDate { get; set; } public string Author { get; set; } - public string Id { get; set; } public string[] Categories { get; set; } public string Content { get; set; } } diff --git a/SharpRss/Models/FeedModel.cs b/SharpRss/Models/FeedModel.cs index 6ebbf1c..a2e1a59 100644 --- a/SharpRss/Models/FeedModel.cs +++ b/SharpRss/Models/FeedModel.cs @@ -4,16 +4,20 @@ namespace SharpRss.Models { public class FeedModel { - public string FeedId { get; set; } - public string GroupId { get; set; } - public string FeedType { get; set; } - public string FeedUrl { get; set; } - public string Description { get; set; } - public string Language { get; set; } - public string Copyright { get; set; } - public DateTime LastUpdated { get; set; } - public string ImageUrl { get; set; } - public int TotalItems { get; set; } - public int TotalRead { get; set; } + public FeedModel(string rssUrl) + { + Id = Guid.NewGuid().ToString(); + Url = rssUrl; + } + public string Id { get; set; } + public string Url { get; set; } + public string? GroupId { get; set; } + public string FeedType { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Language { get; set; } = string.Empty; + public string Copyright { get; set; } = string.Empty; + public DateTimeOffset DateAdded { get; set; } + public DateTimeOffset LastUpdated { get; set; } + public string ImageUrl { get; set; } = string.Empty; } } diff --git a/SharpRss/Models/GroupModel.cs b/SharpRss/Models/GroupModel.cs index 9a5b17f..3af821b 100644 --- a/SharpRss/Models/GroupModel.cs +++ b/SharpRss/Models/GroupModel.cs @@ -5,15 +5,15 @@ namespace SharpRss.Models { public class GroupModel { - public GroupModel(string name) + public GroupModel() { - Name = name; HexColor = Utilities.GenerateRandomHexColor(); Id = Guid.NewGuid().ToString(); } - public string Name { get; set; } + + public string Name { get; set; } = string.Empty; public string HexColor { get; set; } - public string Icon { get; set; } - public string Id { get; private set; } + public string Icon { get; set; } = string.Empty; + public string Id { get; set; } } } diff --git a/SharpRss/Services/DatabaseService.cs b/SharpRss/Services/DatabaseService.cs index c910a97..3e76aa7 100644 --- a/SharpRss/Services/DatabaseService.cs +++ b/SharpRss/Services/DatabaseService.cs @@ -23,86 +23,139 @@ namespace SharpRss.Services private readonly string _feedTable = "feed_data"; private readonly string _feedItemTable = "feed_item_data"; - public async Task RemoveGroupFromFeedsAsync(string groupId) + // Group + public async Task> GetGroupsAsync(string? groupName = null) { - await _sqlConn.QueryAsync("UPDATE feed_data SET group_id=NULL WHERE group_id=@GroupId", new { GroupId = groupId }); - } - - public async Task AddCategoriesAsync(HashSet categories) - { - bool result = true; _sqlConn.Open(); - foreach (var categoryModel in categories) + SqliteCommand cmd = new SqliteCommand(groupName != null ? $"SELECT * FROM {_groupTable} WHERE name=@name;" : $"SELECT * FROM {_groupTable}", _sqlConn) { - await _sqlConn.QueryAsync("INSERT INTO category_data (name, hex_color, path_icon, category_id) VALUES(@catName, @hexColor, @pathIcon, @categoryId) ON CONFLICT(name) DO UPDATE SET hex_color=@hexColor, path_icon=@pathIcon", - new { catName = categoryModel.Name, hexColor = categoryModel.HexColor, pathIcon = categoryModel.Icon, categoryId = categoryModel.Id }); - } - _sqlConn.Close(); - return result; - } - - public async Task AddFeedsAsync(HashSet feeds) - { - bool result = true; - _sqlConn.Open(); - foreach (var feedModel in feeds) - { - await _sqlConn.QueryAsync("INSERT OR REPLACE INTO feed_data(url, feed_id, category_id) VALUES(@url, @feedId, @categoryId) ON CONFLICT(url) DO UPDATE SET category_id=@categoryId", new { url = feedModel.FeedUrl, feedId = feedModel.FeedId, categoryId = feedModel.GroupId }); - } - _sqlConn.Close(); - return result; - } - - public async Task> GetCategoriesAsync() - { - HashSet categories = new HashSet(); - _sqlConn.Open(); - SqliteCommand cmd = _sqlConn.CreateCommand(); - cmd.CommandText = "SELECT * FROM category_data"; + Parameters = + { + new SqliteParameter("name", groupName) + } + }; await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); + HashSet groups = new HashSet(); while (reader.Read()) { - //categories.Add(GroupModel.Create(reader["name"].ToString(), reader["hex_color"].ToString(), reader["category_id"].ToString())); + groups.Add(new GroupModel() + { + Name = reader["name"].ToString(), + HexColor = reader["hex_color"].ToString(), + Icon = reader["icon"].ToString(), + Id = reader["id"].ToString() + }); } - _sqlConn.Close(); - return categories; + return groups; } - public async Task> GetFeedsAsync(string? categoryId = null) + /// + /// Creates a group if not exists else will update the group. + /// + /// + /// + public async Task SetGroupAsync(GroupModel groupModel) + { + bool result = false; + _sqlConn.Open(); + SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_groupTable} (id, hex_color, icon, name) VALUES (IFNULL((SELECT id FROM {_groupTable} WHERE name=@name), @id), @hexColor, @icon, @name)", _sqlConn) + { + Parameters = + { + new SqliteParameter("id", groupModel.Id), + new SqliteParameter("hexColor", groupModel.HexColor), + new SqliteParameter("icon", groupModel.Icon), + new SqliteParameter("name", groupModel.Name) + } + }; + int affected = await cmd.ExecuteNonQueryAsync(); + if (affected != 0) + result = true; + _sqlConn.Close(); + return result; + } + + public async Task RemoveGroupAsync(GroupModel groupModel) + { + bool result = false; + _sqlConn.Open(); + // Remove the group and remove the feeds that were part of the group. + SqliteCommand cmd = new SqliteCommand($"DELETE FROM {_groupTable} WHERE id=@id; UPDATE {_feedTable} SET group_id=NULL WHERE group_id=@id", _sqlConn) + { + Parameters = + { + new SqliteParameter("id", groupModel.Id) + } + }; + int affected = await cmd.ExecuteNonQueryAsync(); + if (affected != 0) + result = true; + _sqlConn.Close(); + return result; + } + // Feed + public async Task> GetFeedsAsync(string? feedName = null) { HashSet feeds = new HashSet(); _sqlConn.Open(); - - SqliteCommand cmd = _sqlConn.CreateCommand(); - cmd.CommandText = categoryId == null ? "SELECT * FROM feed_data" : "SELECT * FROM feed_data WHERE category_id=@categoryId"; - if (categoryId != null) - cmd.Parameters.Add(new SqliteParameter("categoryId", categoryId)); - - await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); - while (reader.Read()) + SqliteCommand cmd = new SqliteCommand(feedName != null ? $"SELECT * FROM {_feedTable} WHERE name=@name" : $"SELECT * FROM {_feedTable}", _sqlConn) { - //feeds.Add(FeedModel.Create(reader["url"].ToString(), reader["feed_id"].ToString(), reader["category_id"].ToString())); - } + Parameters = + { + new SqliteParameter("name", feedName) + } + }; + int affected = await cmd.ExecuteNonQueryAsync(); + Log.Verbose("{FeedAmount} feeds found!", affected); _sqlConn.Close(); return feeds; } + public async Task AddFeedAsync(FeedModel feedModel) + { + bool result = false; + _sqlConn.Open(); + SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedTable} (id, url, group_id, feed_type, description, language, copyright, date_added, last_updated, image_url)" + + $"VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @groupId, @feedType, @description, @language, @copyright, @dateAdded, @lastUpdated, @imageUrl)", _sqlConn) + { + Parameters = + { + new SqliteParameter("id", feedModel.Id), + new SqliteParameter("url", feedModel.Url), + new SqliteParameter("groupId", feedModel.GroupId), + new SqliteParameter("feedType", feedModel.FeedType), + new SqliteParameter("description", feedModel.Description), + new SqliteParameter("language", feedModel.Language), + new SqliteParameter("copyright", feedModel.Copyright), + new SqliteParameter("dateAdded", feedModel.DateAdded.ToUnixTimeMilliseconds()), + new SqliteParameter("lastUpdated", feedModel.LastUpdated.ToUnixTimeMilliseconds()), + new SqliteParameter("imageUrl", feedModel.ImageUrl) + } + }; + int affected = await cmd.ExecuteNonQueryAsync(); + if (affected != 0) + result = true; + _sqlConn.Close(); + return result; + } + // Feed item + private async void InitializeDb() { Log.Verbose("Checking database..."); HashSet failed = new HashSet(); _sqlConn.Open(); Log.Verbose("Checking table: {Table}", _groupTable); - var queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_groupTable} (name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING, id STRING PRIMARY KEY, CONSTRAINT name UNIQUE (name))"); + var queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_groupTable} (name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING, id STRING PRIMARY KEY)"); if (queryResponse.Any()) failed.Add("category_data"); Log.Verbose("Checking table: {Table}", _feedTable); - queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_feedTable} (url STRING NOT NULL, id STRING PRIMARY KEY, group_id STRING DEFAULT NULL, CONSTRAINT url, UNIQUE (url))"); + queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_feedTable} (id STRING PRIMARY KEY, url STRING NOT NULL, group_id STRING DEFAULT NULL, feed_type STRING, description STRING, language STRING, copyright STRING, date_added INT, last_updated INT, image_url STRING)"); if (queryResponse.Any()) failed.Add("feed_data"); Log.Verbose("Checking table: {Table}", _feedItemTable); - queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_feedItemTable} (id STRING PRIMARY KEY, feed_id STRING NOT NULL)"); + queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_feedItemTable} (id STRING PRIMARY KEY, feed_id STRING DEFAULT NULL, read INT, type STRING, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, author STRING, categories STRING, content STRING)"); if (queryResponse.Any()) failed.Add("feed_item_data"); _sqlConn.Close(); diff --git a/SharpRss/Services/RssService.cs b/SharpRss/Services/RssService.cs index 5c06879..639e3e3 100644 --- a/SharpRss/Services/RssService.cs +++ b/SharpRss/Services/RssService.cs @@ -15,13 +15,23 @@ namespace SharpRss.Services { public RssService() { - //SetupTestCategoriesAndFeedsAsync(); + SetupTestCategoriesAndFeedsAsync(); } private readonly DatabaseService _dbService = new DatabaseService(); - /*private async void SetupTestCategoriesAndFeedsAsync() + private async void SetupTestCategoriesAndFeedsAsync() { - await _dbService.AddCategoriesAsync(new HashSet() + /*bool result = await _dbService.SetGroupAsync(new GroupModel() { Name = "News" }); + result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Tech" }); + result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Science" }); + result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Test" });*/ + + /*GroupModel? editGroup = await _dbService.GetGroupsAsync("Test"); + if (editGroup != null) + { + bool result2 = await _dbService.RemoveGroupAsync(editGroup); + }*/ + /*await _dbService.AddCategoriesAsync(new HashSet() { new CategoryModel() { Name = "All" }, new CategoryModel() { Name = "RSS" }, @@ -34,59 +44,7 @@ namespace SharpRss.Services new FeedModel("https://www.nasa.gov/rss/dyn/breaking_news.rss"), new FeedModel("https://journals.plos.org/plosone/feed/atom"), new FeedModel("https://itsfoss.com/feed") - }); - }*/ - - public async Task GetFeedAsync(string rssUrl) - { - /*return await FeedCache.GetFeed(rssUrl);*/ - return new Feed(); - } - - public async Task> GetAllUnsortedAsync() - { - HashSet items = new HashSet(); - /*var categories = await _dbService.GetCategoriesAsync(); - var feeds = await _dbService.GetFeedsAsync(string.Empty); - items.UnionWith(categories); - items.UnionWith(feeds);*/ - return items; - } - - public async Task> GetCategoriesAsync() - { - /*var result = await _dbService.GetCategoriesAsync(); - return result.OrderBy(x => x.Name).ToHashSet();*/ - return new HashSet(); - } - - public async Task> GetFeedsAsync(GroupModel? categoryModel = null) - { - HashSet feeds = new HashSet(); - /*if (categoryModel != null) - feeds = await _dbService.GetFeedsAsync(categoryModel.Id); - else - feeds = await _dbService.GetFeedsAsync();*/ - return feeds; - } - - public async void AddCategoryAsync(string name, string? icon, string hexColor) - { - /*GroupModel groupModel = new GroupModel() - { - Name = name - }; - if (icon != null) - groupModel.PathIcon = icon; - if (!hexColor.IsNullEmptyWhiteSpace()) - groupModel.HexColor = hexColor;*/ - //await _dbService.AddCategoriesAsync(new HashSet() { groupModel }); - } - - public async void AddFeedAsync(string rssUrl, GroupModel? category = null) - { - //FeedModel feedModel = new FeedModel(rssUrl, category); - //await _dbService.AddFeedsAsync(new HashSet() { feedModel }); + });*/ } public void Dispose() diff --git a/SharpRss/SharpRss.csproj b/SharpRss/SharpRss.csproj index 9edc2d0..1103e52 100644 --- a/SharpRss/SharpRss.csproj +++ b/SharpRss/SharpRss.csproj @@ -10,6 +10,7 @@ + diff --git a/WebSharpRSS/sharp_rss.sqlite b/WebSharpRSS/sharp_rss.sqlite index b3bce89a6b314178a70db29d3cbd3f3bda134ad3..4f7eb307d449b8fe059a2e399d100eda50fd4e68 100644 GIT binary patch literal 28672 zcmeI&-)_=S90%}rFi<0_HwF_gc9z9BlGXGdP~wfav1JiaH@GlnX-?ZyXoMD_#O=b1 zY2p+34qo~Wdgl}PDBkHQb`&ya7B9DNN$L61b9#QC(~z`>gBMLdV#Eta0~!%sxFv|9 z@Q4sW5c0{+57RBfzq3g?K8SPa^OAYtfg={ApF-~DXF<}WZ-rkgzixgmd_AuL${_#& z2tWV=5P$##AOL|YFYx}(a=ut9i634^)b2BnG1ubHEd$G?5nasXw)PwKc7wF*kDCp$ zSVuN|mmIYB_gcH;`TpM1`uR3 zo@e~1pXOXPc1Hd%^21DkdskA85n6jI|b~H!pYD|=@)sYLt2pit0fJ(6WUF|op5+O^1HoL zh)ohBTGVwX6O2=4Iv$UxNRlYNKKc#|mH*ZN_$`yCl&WjW0!DWE_T56K&BoDsqikq(jg`G> zMX54b*JxQbbXS$liebuz=9Nv4xz!r~WR~ypRtJvH0*9>|%1&j=WL3AO+EkGh&vs;k z>9%afU1&y?I=oglHQQK9F6Z>tx3{*{hNgQ}uV$AGxmq!G*{CRk(U8W_cNXA z7kU1V*Z=vS{;@#-0uX=z1Rwwb2tWV=5P$##AaIEV;`M(#|6k$>MraU#00bZa0SG_< z0uX=z1RwwbE`ara+y)SU00bZa0SG_<0uX=z1Rwx`%P)ZS|I0teh!6r0fB*y_009U< M00Izz00bcL8)^%MDgXcg delta 283 zcmZp8z}T>WX@WE>GXnzy=R^g2S!M>kWJX^89}FD4$_#w>_z&~<@m}X!%~!#z%sXqd zpuln-&Qf-EaZOFeM%Bq1c@!BrCST@p=JxXsQSb}(@zJ#An0$oSN}01!j)h%ZUY@au zyCg9wCp9fKH6=bJu_O`BV4bYUCowsIPjvDJ9&y(6qWsc=$=tk>^3MK#!68A8o_-+; zrA0Y93ZZ_UfuXJn8bFSwrXky8Mt(J3Be*H-hO9sl18%lPT}F0sMMcIY)yX#eZAv-{ sNao}v=BC2U0dX`njhQDO