Working on db backend reimplementing feed adding to db. Reworking UI for reader

This commit is contained in:
Max 2023-05-25 21:51:05 +02:00
parent b0c9816524
commit 842c21c2b9
4 changed files with 69 additions and 44 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -116,7 +117,15 @@ namespace SharpRss.Services
await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
while (reader.Read()) while (reader.Read())
{ {
feeds.Add(new FeedModel(reader["url"].ToString()) feeds.Add(ReaderToFeedModel(reader));
}
_sqlConn.Close();
return feeds;
}
private FeedModel ReaderToFeedModel(SqliteDataReader reader)
{
return new FeedModel(reader["url"].ToString())
{ {
Id = reader["id"].ToString(), Id = reader["id"].ToString(),
Title = reader["title"].ToString(), Title = reader["title"].ToString(),
@ -129,17 +138,14 @@ namespace SharpRss.Services
LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["last_updated"].ToString(), out long lastUpdated) ? lastUpdated : 0), LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.TryParse(reader["last_updated"].ToString(), out long lastUpdated) ? lastUpdated : 0),
ImageUrl = reader["image_url"].ToString(), ImageUrl = reader["image_url"].ToString(),
OriginalDocument = reader["original_document"].ToString() OriginalDocument = reader["original_document"].ToString()
}); };
}
_sqlConn.Close();
return feeds;
} }
public async Task<bool> SetFeedAsync(FeedModel feedModel) public async Task<FeedModel?> SetFeedAsync(FeedModel feedModel)
{ {
bool result = false; FeedModel? resultModel = null;
_sqlConn.Open(); _sqlConn.Open();
await using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedTable} (id, url, title, group_id, feed_type, description, language, copyright, date_added, last_updated, image_url, original_document) VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @title, @groupId, @feedType, @description, @language, @copyright, IFNULL((SELECT date_added FROM {_feedItemTable} WHERE id=@id), @dateAdded), @lastUpdated, @imageUrl, @originalDoc)", _sqlConn) await using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedTable} (id, url, title, group_id, feed_type, description, language, copyright, date_added, last_updated, image_url, original_document) VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @title, @groupId, @feedType, @description, @language, @copyright, IFNULL((SELECT date_added FROM {_feedTable} WHERE id=@id), @dateAdded), @lastUpdated, @imageUrl, @originalDoc); SELECT * FROM {_feedTable} WHERE url=@url", _sqlConn)
{ {
Parameters = Parameters =
{ {
@ -157,11 +163,11 @@ namespace SharpRss.Services
new SqliteParameter("originalDoc", feedModel.OriginalDocument ?? string.Empty) new SqliteParameter("originalDoc", feedModel.OriginalDocument ?? string.Empty)
} }
}; };
int affected = await cmd.ExecuteNonQueryAsync(); await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
if (affected != 0) if (reader.Read())
result = true; resultModel = ReaderToFeedModel(reader);
_sqlConn.Close(); _sqlConn.Close();
return result; return resultModel;
} }
public async Task<bool> RemoveFeedAsync(FeedModel feedModel) public async Task<bool> RemoveFeedAsync(FeedModel feedModel)
{ {
@ -183,14 +189,13 @@ namespace SharpRss.Services
// Feed items // Feed items
public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync(string[]? feedIds = null) public async Task<HashSet<FeedItemModel>> GetFeedItemsAsync(string[]? feedIds = null)
{ {
List<string>? formattedIds = feedIds?.Select(s => $"'{s}'").ToList();
HashSet<FeedItemModel> feedItems = new HashSet<FeedItemModel>(); HashSet<FeedItemModel> feedItems = new HashSet<FeedItemModel>();
_sqlConn.Open(); _sqlConn.Open();
await using SqliteCommand cmd = new SqliteCommand( await using SqliteCommand cmd = new SqliteCommand(
feedIds != null formattedIds != null
? $"SELECT * FROM {_feedItemTable} WHERE feed_id IN (@feedIds)" ? $"SELECT * FROM {_feedItemTable} WHERE feed_id IN ({string.Join(", ", formattedIds)})"
: $"SELECT * FROM {_feedItemTable}", _sqlConn); : $"SELECT * FROM {_feedItemTable}", _sqlConn);
if (feedIds != null)
cmd.Parameters.Add(new SqliteParameter("feedIds", string.Join(',', feedIds)));
await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
while (reader.Read()) while (reader.Read())
@ -217,6 +222,7 @@ namespace SharpRss.Services
{ {
int result = 0; int result = 0;
_sqlConn.Open(); _sqlConn.Open();
await using SqliteTransaction transaction = _sqlConn.BeginTransaction();
await using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedItemTable} (id, feed_id, read, title, description, link, last_updated, publishing_date, author, categories, content)" + await using SqliteCommand cmd = new SqliteCommand($"INSERT OR REPLACE INTO {_feedItemTable} (id, feed_id, read, title, description, link, last_updated, publishing_date, author, categories, content)" +
$"VALUES (IFNULL((SELECT id FROM {_feedItemTable} WHERE link=@link), @id), @feedId, @read, @title, @description, @link, @lastUpdated, @publishingDate, @author, @categories, @content)", _sqlConn); $"VALUES (IFNULL((SELECT id FROM {_feedItemTable} WHERE link=@link), @id), @feedId, @read, @title, @description, @link, @lastUpdated, @publishingDate, @author, @categories, @content)", _sqlConn);
foreach (FeedItemModel item in items) foreach (FeedItemModel item in items)
@ -234,12 +240,15 @@ namespace SharpRss.Services
cmd.Parameters.Add(new SqliteParameter("author", item.Author ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("author", item.Author ?? string.Empty));
cmd.Parameters.Add(new SqliteParameter("categories", item.Categories != null ? string.Join(',', item.Categories) : string.Empty)); cmd.Parameters.Add(new SqliteParameter("categories", item.Categories != null ? string.Join(',', item.Categories) : string.Empty));
cmd.Parameters.Add(new SqliteParameter("content", item.Content ?? string.Empty)); cmd.Parameters.Add(new SqliteParameter("content", item.Content ?? string.Empty));
if (_sqlConn.State != ConnectionState.Open)
_sqlConn.Open();
int affected = await cmd.ExecuteNonQueryAsync(); int affected = await cmd.ExecuteNonQueryAsync();
if (affected == 0) if (affected == 0)
Log.Verbose("Could not set feed item: {FeedLink}", item.Link); Log.Verbose("Could not set feed item: {FeedLink}", item.Link);
else else
result += affected; result += affected;
} }
transaction.Commit();
_sqlConn.Close(); _sqlConn.Close();
return result; // Return the amount affected rows. return result; // Return the amount affected rows.
} }

View File

@ -16,7 +16,7 @@ namespace SharpRss.Services
{ {
public RssService() public RssService()
{ {
//SetupTestCategoriesAndFeedsAsync(); SetupTestGroupsAndFeedsAsync();
} }
private readonly DatabaseService _dbService = new DatabaseService(); private readonly DatabaseService _dbService = new DatabaseService();
@ -50,11 +50,12 @@ namespace SharpRss.Services
ImageUrl = fetched.ImageUrl, ImageUrl = fetched.ImageUrl,
OriginalDocument = fetched.OriginalDocument OriginalDocument = fetched.OriginalDocument
}; };
result = await _dbService.SetFeedAsync(feedModel); FeedModel? dbFeed = await _dbService.SetFeedAsync(feedModel);
if (!result) result = dbFeed != null;
if (dbFeed == null)
return result; return result;
if (await AddFeedItems(fetched.Items, feedModel) == 0) if (await AddFeedItems(fetched.Items, dbFeed) == 0)
Log.Warning("No feed items added to feed: {FeedUrl}", feedModel.Url); Log.Warning("No feed items added to feed: {FeedUrl}", dbFeed.Url);
return result; return result;
} }
public async Task<HashSet<FeedModel>> GetFeedsAsync(string? groupId = null) => await _dbService.GetFeedsAsync(groupId); public async Task<HashSet<FeedModel>> GetFeedsAsync(string? groupId = null) => await _dbService.GetFeedsAsync(groupId);
@ -106,24 +107,38 @@ namespace SharpRss.Services
} }
private async Task<Feed> FetchFeed(string url) private async Task<Feed> FetchFeed(string url)
{ {
Log.Verbose("Fetching feed: {FeedUrl}", url);
var urls = await FeedReader.ParseFeedUrlsAsStringAsync(url); var urls = await FeedReader.ParseFeedUrlsAsStringAsync(url);
string feedurl = url; string feedUrl = url;
if (urls.Any()) if (urls.Any())
feedurl = urls.First(); feedUrl = urls.First();
return await FeedReader.ReadAsync(feedurl); return await FeedReader.ReadAsync(feedUrl);
} }
private async void SetupTestCategoriesAndFeedsAsync() private async void SetupTestGroupsAndFeedsAsync()
{ {
var groupRes = await CreateGroupAsync(new GroupModel() { Name = "Test" }); //TODO: Make multiple adding of feed to a transaction, now throws an exception.
/*var groupRes = await CreateGroupAsync(new GroupModel() { Name = "Test" });
groupRes = await CreateGroupAsync(new GroupModel() { Name = "News" }); groupRes = await CreateGroupAsync(new GroupModel() { Name = "News" });
groupRes = await CreateGroupAsync(new GroupModel() { Name = "Tech" }); groupRes = await CreateGroupAsync(new GroupModel() { Name = "Tech" });
groupRes = await CreateGroupAsync(new GroupModel() { Name = "Science" }); groupRes = await CreateGroupAsync(new GroupModel() { Name = "Science" });*/
var groups = await GetGroupsAsync(); /*var groups = await GetGroupsAsync();
GroupModel testGroup = groups.Single(x => x.Name == "Test"); GroupModel testGroup = groups.Single(x => x.Name == "Test");
await Task.Run(async () =>
{
try
{
var res = await AddFeed("http://fedoramagazine.org/feed/", testGroup); 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://www.nasa.gov/rss/dyn/breaking_news.rss", testGroup);
res = await AddFeed("https://journals.plos.org/plosone/feed/atom", testGroup); res = await AddFeed("https://journals.plos.org/plosone/feed/atom", testGroup);
res = await AddFeed("https://itsfoss.com/feed", testGroup); res = await AddFeed("https://itsfoss.com/feed", testGroup);
res = await AddFeed("https://advisories.ncsc.nl/rss/advisories", testGroup);
}
catch (Exception e)
{
Log.Error(e, "Error fetching feeds!");
throw;
}
});*/
} }
public void Dispose() public void Dispose()

View File

@ -76,12 +76,12 @@
bool faulted = false; bool faulted = false;
private async void LoadItems() private async void LoadItems()
{ {
//TODO: better group feed fetching!
faulted = false; faulted = false;
isLoading = true;
if (Fid != null) if (Fid != null)
{ {
var fItems = await _rssService.GetFeedItemsAsync(Fid); var fItems = await _rssService.GetFeedItemsAsync(Fid);
items = fItems.Select(x => FeedItemData.FromModel(x)).ToHashSet(); items = fItems.Select(x => FeedItemData.FromModel(x)).OrderBy(x => x.PublishingDate).Reverse().ToHashSet();
isLoading = false; isLoading = false;
} }
else if (Gid != null) else if (Gid != null)
@ -89,12 +89,13 @@
var feeds = await _rssService.GetFeedsAsync(Gid); var feeds = await _rssService.GetFeedsAsync(Gid);
var feedids = feeds.Select(x => x.Id); var feedids = feeds.Select(x => x.Id);
var feedItems = await _rssService.GetFeedItemsFromFeedsAsync(feedids.ToArray()); var feedItems = await _rssService.GetFeedItemsFromFeedsAsync(feedids.ToArray());
items = feedItems.Select(x => FeedItemData.FromModel(x)).ToHashSet(); items = feedItems.Select(x => FeedItemData.FromModel(x)).OrderBy(x => x.PublishingDate).Reverse().ToHashSet();
} }
else else
{ {
faulted = true; faulted = true;
} }
isLoading = false;
StateHasChanged(); StateHasChanged();
} }
protected override void OnInitialized() protected override void OnInitialized()

Binary file not shown.