From 29f9abd5de7abe26101dc9561db69e8adaab2880 Mon Sep 17 00:00:00 2001 From: Max Holleman Date: Mon, 22 May 2023 15:55:21 +0200 Subject: [PATCH] Implenting rssservice --- SharpRss/Models/FeedItemModel.cs | 7 +-- SharpRss/Models/FeedModel.cs | 13 +++-- SharpRss/Services/DatabaseService.cs | 44 ++++++++++------ SharpRss/Services/RssService.cs | 75 ++++++++++++++++++++++++++- ToolQit | 2 +- WebSharpRSS/Models/TreeItemData.cs | 56 -------------------- WebSharpRSS/Shared/SideGuide.razor | 14 ++--- WebSharpRSS/sharp_rss.sqlite | Bin 28672 -> 28672 bytes 8 files changed, 119 insertions(+), 92 deletions(-) diff --git a/SharpRss/Models/FeedItemModel.cs b/SharpRss/Models/FeedItemModel.cs index 99260bc..47c60e3 100644 --- a/SharpRss/Models/FeedItemModel.cs +++ b/SharpRss/Models/FeedItemModel.cs @@ -4,12 +4,7 @@ namespace SharpRss.Models { public class FeedItemModel { - public FeedItemModel() - { - Id = Guid.NewGuid().ToString(); - LastUpdated = DateTime.Now; - } - public string Id { get; set; } + public string Id { get; set; } = string.Empty; public string? FeedId { get; set; } public bool Read { get; set; } public string Type { get; set; } = string.Empty; diff --git a/SharpRss/Models/FeedModel.cs b/SharpRss/Models/FeedModel.cs index a2e1a59..c580850 100644 --- a/SharpRss/Models/FeedModel.cs +++ b/SharpRss/Models/FeedModel.cs @@ -4,20 +4,23 @@ namespace SharpRss.Models { public class FeedModel { + public FeedModel() { } public FeedModel(string rssUrl) { - Id = Guid.NewGuid().ToString(); Url = rssUrl; + Id = Guid.NewGuid().ToString(); + Copyright = "EMPTY"; + ImageUrl = "EMPTY"; } - public string Id { get; set; } - public string Url { get; set; } + public string Id { get; set; } = string.Empty; + public string Url { get; set; } = string.Empty; 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 string Copyright { get; set; } = "EMPTY"; public DateTimeOffset DateAdded { get; set; } public DateTimeOffset LastUpdated { get; set; } - public string ImageUrl { get; set; } = string.Empty; + public string ImageUrl { get; set; } = "EMPTY"; } } diff --git a/SharpRss/Services/DatabaseService.cs b/SharpRss/Services/DatabaseService.cs index a72d712..a5b34eb 100644 --- a/SharpRss/Services/DatabaseService.cs +++ b/SharpRss/Services/DatabaseService.cs @@ -23,8 +23,8 @@ namespace SharpRss.Services private readonly string _feedTable = "feed_data"; private readonly string _feedItemTable = "feed_item_data"; - // Group - public async Task> GetGroupsAsync(string? groupName = null) + // Groups + public async Task> GetGroupsAsync(string? groupName = null) { _sqlConn.Open(); using SqliteCommand cmd = new SqliteCommand(groupName != null ? $"SELECT * FROM {_groupTable} WHERE name=@name;" : $"SELECT * FROM {_groupTable}", _sqlConn) @@ -35,7 +35,7 @@ namespace SharpRss.Services } }; await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); - HashSet groups = new HashSet(); + HashSet groups = new HashSet(); while (reader.Read()) { groups.Add(new GroupModel() @@ -51,7 +51,7 @@ namespace SharpRss.Services } /// - /// Creates a group if not exists else will update the group. + /// Creates a group if not exists then update the group. /// /// /// @@ -94,20 +94,35 @@ namespace SharpRss.Services _sqlConn.Close(); return result; } - // Feed - public async Task> GetFeedsAsync(string? feedName = null) + // Feeds + public async Task> GetFeedsAsync(GroupModel? group = null) { HashSet feeds = new HashSet(); _sqlConn.Open(); - using SqliteCommand cmd = new SqliteCommand(feedName != null ? $"SELECT * FROM {_feedTable} WHERE name=@name" : $"SELECT * FROM {_feedTable}", _sqlConn) + using SqliteCommand cmd = new SqliteCommand(group != null ? $"SELECT * FROM {_feedTable} WHERE group_id=@groupid" : $"SELECT * FROM {_feedTable}", _sqlConn) { Parameters = { - new SqliteParameter("name", feedName) + new SqliteParameter("groupId", group?.Id == string.Empty ? null : group?.Id) } }; - int affected = await cmd.ExecuteNonQueryAsync(); - Log.Verbose("{FeedAmount} feeds found!", affected); + await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); + while (reader.Read()) + { + feeds.Add(new FeedModel() + { + Id = reader["id"].ToString(), + Url = reader["url"].ToString(), + GroupId = reader["group_id"].ToString(), + FeedType = reader["feed_type"].ToString(), + Description = reader["description"].ToString(), + Language = reader["language"].ToString(), + Copyright = reader["copyright"].ToString(), + DateAdded = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["date_added"].ToString(), out long parsedVal) ? parsedVal : 0), + LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["last_updated"].ToString(), out long lastUpdated) ? lastUpdated : 0), + ImageUrl = reader["image_url"].ToString() + }); + } _sqlConn.Close(); return feeds; } @@ -116,8 +131,7 @@ namespace SharpRss.Services { bool result = false; _sqlConn.Open(); - using 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) + using 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 = { @@ -127,10 +141,10 @@ namespace SharpRss.Services new SqliteParameter("feedType", feedModel.FeedType), new SqliteParameter("description", feedModel.Description), new SqliteParameter("language", feedModel.Language), - new SqliteParameter("copyright", feedModel.Copyright), + new SqliteParameter("copyright", "EMPTY"), new SqliteParameter("dateAdded", feedModel.DateAdded.ToUnixTimeMilliseconds()), new SqliteParameter("lastUpdated", feedModel.LastUpdated.ToUnixTimeMilliseconds()), - new SqliteParameter("imageUrl", feedModel.ImageUrl) + new SqliteParameter("imageUrl", "EMPTY") } }; int affected = await cmd.ExecuteNonQueryAsync(); @@ -156,7 +170,7 @@ namespace SharpRss.Services _sqlConn.Close(); return result; } - // Feed item + // Feed items public async Task> GetFeedItemsAsync() { HashSet feeditems = new HashSet (); diff --git a/SharpRss/Services/RssService.cs b/SharpRss/Services/RssService.cs index 639e3e3..198867d 100644 --- a/SharpRss/Services/RssService.cs +++ b/SharpRss/Services/RssService.cs @@ -3,24 +3,95 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CodeHollow.FeedReader; +using Serilog; using SharpRss.Models; using ToolQit.Extensions; namespace SharpRss.Services { /// - /// Managing RSS feeds and categories. + /// Managing RSS feeds and groups. /// public class RssService : IDisposable { public RssService() { - SetupTestCategoriesAndFeedsAsync(); + SetupTestCategoriesAndFeedsAsync(); } private readonly DatabaseService _dbService = new DatabaseService(); + + public async Task CreateGroupAsync(GroupModel group) => await _dbService.SetGroupAsync(group); + public async Task> GetGroupsAsync() => await _dbService.GetGroupsAsync(); + + public async Task AddFeed(string rssUrl, GroupModel? group = null) + { + bool result = false; + Feed fetched = await FetchFeed(rssUrl); + if (fetched == null) + return result; + FeedModel feedModel = new FeedModel(rssUrl) + { + GroupId = group?.Id, + FeedType = fetched.Type.ToString(), + Description = fetched.Description, + Language = fetched.Language, + Copyright = fetched.Copyright, + DateAdded = DateTimeOffset.Now, + LastUpdated = DateTimeOffset.Now, + ImageUrl = fetched.ImageUrl, + }; + result = await _dbService.SetFeedAsync(feedModel); + if (!result) + return result; + if (await AddFeedItems(fetched.Items, feedModel) == 0) + Log.Warning($"No feed items added to feed: {feedModel.Url}"); + return result; + } + public async Task> GetFeedsAsync(GroupModel? group = null) => await _dbService.GetFeedsAsync(group); + public async Task> GetUngroupedFeedsAsync() => await _dbService.GetFeedsAsync(new GroupModel() { Id = "" }); + private async Task AddFeedItems(IList items, FeedModel feedModel) + { + int result = 0; + if (!items.Any()) + return result; + HashSet itemModels = new HashSet(); + foreach (FeedItem item in items) + { + itemModels.Add(new FeedItemModel() + { + Id = item.Id, + FeedId = feedModel.Id, + Title = item.Title, + Description = item.Description, + Link = item.Link, + LastUpdated = DateTimeOffset.Now, + PublishingDate = item.PublishingDate != null ? new DateTimeOffset((DateTime)item.PublishingDate) : null, + Author = item.Author, + Categories = item.Categories.ToArray(), + Content = item.Content + }); + } + result = await _dbService.SetFeedItemsAsync(itemModels); + return result; + } + private async Task FetchFeed(string url) + { + var urls = await FeedReader.ParseFeedUrlsAsStringAsync(url); + string feedurl = url; + if (urls.Any()) + feedurl = urls.First(); + return await FeedReader.ReadAsync(feedurl); + } private async void SetupTestCategoriesAndFeedsAsync() { + var groups = await GetGroupsAsync(); + GroupModel testGroup = groups.Single(x => x.Name == "Test"); + var res = await AddFeed("http://fedoramagazine.org/feed/", testGroup); + res = await AddFeed("https://www.nasa.gov/rss/dyn/breaking_news.rss", testGroup); + res = await AddFeed("https://journals.plos.org/plosone/feed/atom", testGroup); + res = await AddFeed("https://itsfoss.com/feed", testGroup); + /*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" }); diff --git a/ToolQit b/ToolQit index af3abae..0f3a3fb 160000 --- a/ToolQit +++ b/ToolQit @@ -1 +1 @@ -Subproject commit af3abae0445b30c3ef58579a18e6e179eaea5986 +Subproject commit 0f3a3fb0f9145aad31a44eb25159d0f2a5a1c7fd diff --git a/WebSharpRSS/Models/TreeItemData.cs b/WebSharpRSS/Models/TreeItemData.cs index 8ccd382..a7a16b0 100644 --- a/WebSharpRSS/Models/TreeItemData.cs +++ b/WebSharpRSS/Models/TreeItemData.cs @@ -9,21 +9,17 @@ namespace WebSharpRSS.Models { _service = rssService; CategoryModel = catModel; - Initialize(); } public TreeItemData(FeedModel feedModel, RssService rssService) { _service = rssService; FeedModel = feedModel; - - Initialize(); } private readonly RssService _service; public readonly GroupModel? CategoryModel; public readonly FeedModel? FeedModel; - //private HashSet _feedModels; public string Title { get; set; } = string.Empty; public bool IsSelected { get; set; } @@ -33,57 +29,5 @@ namespace WebSharpRSS.Models // Category public bool HasChild { get; set; } public bool IsExpanded { get; set; } - //public HashSet? Feeds { get; set; } - // Feed - //public Feed? Feed { get; private set; } - /*public int FeeditemCount - { - get - { - if (CategoryModel != null && Feeds != null) - { - int feedsCount = 0; - foreach (var item in Feeds) - { - if (item.Feed != null) - feedsCount += item.FeeditemCount; - } - return feedsCount; - } - else if (Feed != null) - return Feed.Items.Count; - return 0; - } - }*/ - - private async void Initialize() - { - /*if (CategoryModel != null) - { - Title = CategoryModel.Name; - Icon = Icons.Material.Filled.Category; - /*_feedModels = await _service.GetFeedsAsync(CategoryModel); - if (_feedModels.Any()) - Feeds = _feedModels.Select(x => new TreeItemData(x, _service)).OrderBy(x => x.Title).ToHashSet();#1# - } - - if (FeedModel != null) - { - try - { - //Feed = await _service.GetFeedAsync(FeedModel.FeedUrl); - } - catch (Exception e) - { - Log.Error(e, "Error fetching feed: {FeedUrl}", FeedModel.FeedUrl); - Feed = null; - return; - } - Icon = Icons.Material.Filled.RssFeed; - Title = Feed.Title; - string faviconAddress = Feed.Link.Remove(Feed.Link.IndexOf("http", StringComparison.Ordinal), Feed.Link.IndexOf("://", StringComparison.Ordinal) + 3); - FaviconUrl = string.Format(Caretaker.Settings["Paths"].GetString("FaviconResolveUrl"), faviconAddress); - }*/ - } } } \ No newline at end of file diff --git a/WebSharpRSS/Shared/SideGuide.razor b/WebSharpRSS/Shared/SideGuide.razor index 7a79454..486acd4 100644 --- a/WebSharpRSS/Shared/SideGuide.razor +++ b/WebSharpRSS/Shared/SideGuide.razor @@ -9,9 +9,9 @@ @inject FeedStateContainer _stateContainer; @inject RssService _rssService - - @* - $1$ +@* + +
@@ -33,13 +33,13 @@
-
#1# -
*@ -
+
+
+
*@ @code { private MudTheme Theme = new MudTheme(); - //private readonly HashSet _guideItems = new HashSet(); + private readonly HashSet _guideItems = new HashSet(); /*private TreeItemData? _selectedItem; private TreeItemData? SelectedItem { diff --git a/WebSharpRSS/sharp_rss.sqlite b/WebSharpRSS/sharp_rss.sqlite index 4f7eb307d449b8fe059a2e399d100eda50fd4e68..c57d0c7e1477875bfb6de395fcd07bcb6f0b7078 100644 GIT binary patch delta 345 zcmZp8z}WDBae_1>$3z)tMvjdMOZ3?o`JOQFJ=rWMaEmXfNs^H#)H_tVNmN{%l~vq4 z*}%}$+|*Pz#l*r`*Cff@K-a?1GELXiFxkX1DKW_`)!ZPXq@=(~Uq3B1CBG;!H!(f2 zDl;!tFTW@qD3qF_KRHofr9P;*INm7Uz`ZmxCAC;bAu}&6zbH4cBr`uxM zyjUSIDZjKtp(Gn>1(1?`OveX=q0w5bI zo0yWDnU`5y0@RZa5(XKZoS&OpnrE0`$QRMM)r*fOY}Fh1pJX-6rcbAhWiPI