Implenting rssservice

This commit is contained in:
Max Holleman 2023-05-22 15:55:21 +02:00
parent f83b4451a7
commit 29f9abd5de
8 changed files with 119 additions and 92 deletions

View File

@ -4,12 +4,7 @@ namespace SharpRss.Models
{ {
public class FeedItemModel public class FeedItemModel
{ {
public FeedItemModel() public string Id { get; set; } = string.Empty;
{
Id = Guid.NewGuid().ToString();
LastUpdated = DateTime.Now;
}
public string Id { get; set; }
public string? FeedId { get; set; } public string? FeedId { get; set; }
public bool Read { get; set; } public bool Read { get; set; }
public string Type { get; set; } = string.Empty; public string Type { get; set; } = string.Empty;

View File

@ -4,20 +4,23 @@ namespace SharpRss.Models
{ {
public class FeedModel public class FeedModel
{ {
public FeedModel() { }
public FeedModel(string rssUrl) public FeedModel(string rssUrl)
{ {
Id = Guid.NewGuid().ToString();
Url = rssUrl; Url = rssUrl;
Id = Guid.NewGuid().ToString();
Copyright = "EMPTY";
ImageUrl = "EMPTY";
} }
public string Id { get; set; } public string Id { get; set; } = string.Empty;
public string Url { get; set; } public string Url { get; set; } = string.Empty;
public string? GroupId { get; set; } public string? GroupId { get; set; }
public string FeedType { get; set; } = string.Empty; public string FeedType { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
public string Language { 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 DateAdded { get; set; }
public DateTimeOffset LastUpdated { get; set; } public DateTimeOffset LastUpdated { get; set; }
public string ImageUrl { get; set; } = string.Empty; public string ImageUrl { get; set; } = "EMPTY";
} }
} }

View File

@ -23,8 +23,8 @@ namespace SharpRss.Services
private readonly string _feedTable = "feed_data"; private readonly string _feedTable = "feed_data";
private readonly string _feedItemTable = "feed_item_data"; private readonly string _feedItemTable = "feed_item_data";
// Group // Groups
public async Task<HashSet<GroupModel?>> GetGroupsAsync(string? groupName = null) public async Task<HashSet<GroupModel>> GetGroupsAsync(string? groupName = null)
{ {
_sqlConn.Open(); _sqlConn.Open();
using SqliteCommand cmd = new SqliteCommand(groupName != null ? $"SELECT * FROM {_groupTable} WHERE name=@name;" : $"SELECT * FROM {_groupTable}", _sqlConn) 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(); await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
HashSet<GroupModel?> groups = new HashSet<GroupModel?>(); HashSet<GroupModel> groups = new HashSet<GroupModel>();
while (reader.Read()) while (reader.Read())
{ {
groups.Add(new GroupModel() groups.Add(new GroupModel()
@ -51,7 +51,7 @@ namespace SharpRss.Services
} }
/// <summary> /// <summary>
/// Creates a group if not exists else will update the group. /// Creates a group if not exists then update the group.
/// </summary> /// </summary>
/// <param name="groupModel"></param> /// <param name="groupModel"></param>
/// <returns></returns> /// <returns></returns>
@ -94,20 +94,35 @@ namespace SharpRss.Services
_sqlConn.Close(); _sqlConn.Close();
return result; return result;
} }
// Feed // Feeds
public async Task<HashSet<FeedModel>> GetFeedsAsync(string? feedName = null) public async Task<HashSet<FeedModel>> GetFeedsAsync(GroupModel? group = null)
{ {
HashSet<FeedModel> feeds = new HashSet<FeedModel>(); HashSet<FeedModel> feeds = new HashSet<FeedModel>();
_sqlConn.Open(); _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 = Parameters =
{ {
new SqliteParameter("name", feedName) new SqliteParameter("groupId", group?.Id == string.Empty ? null : group?.Id)
} }
}; };
int affected = await cmd.ExecuteNonQueryAsync(); await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
Log.Verbose("{FeedAmount} feeds found!", affected); 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(); _sqlConn.Close();
return feeds; return feeds;
} }
@ -116,8 +131,7 @@ namespace SharpRss.Services
{ {
bool result = false; bool result = false;
_sqlConn.Open(); _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)" + 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)
$"VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @groupId, @feedType, @description, @language, @copyright, @dateAdded, @lastUpdated, @imageUrl)", _sqlConn)
{ {
Parameters = Parameters =
{ {
@ -127,10 +141,10 @@ namespace SharpRss.Services
new SqliteParameter("feedType", feedModel.FeedType), new SqliteParameter("feedType", feedModel.FeedType),
new SqliteParameter("description", feedModel.Description), new SqliteParameter("description", feedModel.Description),
new SqliteParameter("language", feedModel.Language), new SqliteParameter("language", feedModel.Language),
new SqliteParameter("copyright", feedModel.Copyright), new SqliteParameter("copyright", "EMPTY"),
new SqliteParameter("dateAdded", feedModel.DateAdded.ToUnixTimeMilliseconds()), new SqliteParameter("dateAdded", feedModel.DateAdded.ToUnixTimeMilliseconds()),
new SqliteParameter("lastUpdated", feedModel.LastUpdated.ToUnixTimeMilliseconds()), new SqliteParameter("lastUpdated", feedModel.LastUpdated.ToUnixTimeMilliseconds()),
new SqliteParameter("imageUrl", feedModel.ImageUrl) new SqliteParameter("imageUrl", "EMPTY")
} }
}; };
int affected = await cmd.ExecuteNonQueryAsync(); int affected = await cmd.ExecuteNonQueryAsync();
@ -156,7 +170,7 @@ namespace SharpRss.Services
_sqlConn.Close(); _sqlConn.Close();
return result; return result;
} }
// Feed item // Feed items
public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync() public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync()
{ {
HashSet<FeedItemModel> feeditems = new HashSet<FeedItemModel> (); HashSet<FeedItemModel> feeditems = new HashSet<FeedItemModel> ();

View File

@ -3,13 +3,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using CodeHollow.FeedReader; using CodeHollow.FeedReader;
using Serilog;
using SharpRss.Models; using SharpRss.Models;
using ToolQit.Extensions; using ToolQit.Extensions;
namespace SharpRss.Services namespace SharpRss.Services
{ {
/// <summary> /// <summary>
/// Managing RSS feeds and categories. /// Managing RSS feeds and groups.
/// </summary> /// </summary>
public class RssService : IDisposable public class RssService : IDisposable
{ {
@ -19,8 +20,78 @@ namespace SharpRss.Services
} }
private readonly DatabaseService _dbService = new DatabaseService(); private readonly DatabaseService _dbService = new DatabaseService();
public async Task<bool> CreateGroupAsync(GroupModel group) => await _dbService.SetGroupAsync(group);
public async Task<HashSet<GroupModel>> GetGroupsAsync() => await _dbService.GetGroupsAsync();
public async Task<bool> 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<HashSet<FeedModel>> GetFeedsAsync(GroupModel? group = null) => await _dbService.GetFeedsAsync(group);
public async Task<HashSet<FeedModel>> GetUngroupedFeedsAsync() => await _dbService.GetFeedsAsync(new GroupModel() { Id = "" });
private async Task<int> AddFeedItems(IList<FeedItem> items, FeedModel feedModel)
{
int result = 0;
if (!items.Any())
return result;
HashSet<FeedItemModel> itemModels = new HashSet<FeedItemModel>();
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<Feed> 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() 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" }); /*bool result = await _dbService.SetGroupAsync(new GroupModel() { Name = "News" });
result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Tech" }); result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Tech" });
result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Science" }); result = await _dbService.SetGroupAsync(new GroupModel() { Name = "Science" });

@ -1 +1 @@
Subproject commit af3abae0445b30c3ef58579a18e6e179eaea5986 Subproject commit 0f3a3fb0f9145aad31a44eb25159d0f2a5a1c7fd

View File

@ -9,21 +9,17 @@ namespace WebSharpRSS.Models
{ {
_service = rssService; _service = rssService;
CategoryModel = catModel; CategoryModel = catModel;
Initialize();
} }
public TreeItemData(FeedModel feedModel, RssService rssService) public TreeItemData(FeedModel feedModel, RssService rssService)
{ {
_service = rssService; _service = rssService;
FeedModel = feedModel; FeedModel = feedModel;
Initialize();
} }
private readonly RssService _service; private readonly RssService _service;
public readonly GroupModel? CategoryModel; public readonly GroupModel? CategoryModel;
public readonly FeedModel? FeedModel; public readonly FeedModel? FeedModel;
//private HashSet<FeedModel> _feedModels;
public string Title { get; set; } = string.Empty; public string Title { get; set; } = string.Empty;
public bool IsSelected { get; set; } public bool IsSelected { get; set; }
@ -33,57 +29,5 @@ namespace WebSharpRSS.Models
// Category // Category
public bool HasChild { get; set; } public bool HasChild { get; set; }
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
//public HashSet<TreeItemData>? 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);
}*/
}
} }
} }

View File

@ -9,9 +9,9 @@
@inject FeedStateContainer _stateContainer; @inject FeedStateContainer _stateContainer;
@inject RssService _rssService @inject RssService _rssService
<MudStack Spacing="2"> @*<MudStack Spacing="2">
@*<MudTreeView Items="_guideItems" @bind-SelectedValue="SelectedItem" Hover="true"> <MudTreeView Items="_guideItems" @bind-SelectedValue="SelectedItem" Hover="true">
$1$<ItemTemplate> <ItemTemplate>
<MudTreeViewItem @bind-Expanded="@context.IsExpanded" Items="@context.Feeds" Value="@context" CanExpand="@context.HasChild" @onclick="ItemClicked"> <MudTreeViewItem @bind-Expanded="@context.IsExpanded" Items="@context.Feeds" Value="@context" CanExpand="@context.HasChild" @onclick="ItemClicked">
<Content> <Content>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%"> <div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
@ -33,13 +33,13 @@
</div> </div>
</Content> </Content>
</MudTreeViewItem> </MudTreeViewItem>
</ItemTemplate>#1# </ItemTemplate>
</MudTreeView>*@ </MudTreeView>
</MudStack> </MudStack>*@
@code { @code {
private MudTheme Theme = new MudTheme(); private MudTheme Theme = new MudTheme();
//private readonly HashSet<TreeItemData> _guideItems = new HashSet<TreeItemData>(); private readonly HashSet<TreeItemData> _guideItems = new HashSet<TreeItemData>();
/*private TreeItemData? _selectedItem; /*private TreeItemData? _selectedItem;
private TreeItemData? SelectedItem private TreeItemData? SelectedItem
{ {

Binary file not shown.