...
 
Commits (6)
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
......@@ -7,6 +8,7 @@ using MySocialPortalDesktop.Views;
namespace MySocialPortalDesktop
{
[SuppressMessage("ReSharper", "CA1303")]
public class App : Application
{
public override void Initialize()
......
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Avalonia.Media.Imaging;
using MySocialPortalDesktop.Factory;
......@@ -9,16 +10,16 @@ using MySocialPortalLib.Service;
namespace MySocialPortalDesktop.Services
{
[SuppressMessage("ReSharper", "CA1031")]
public static class DefaultValueService
{
private const string DefaultProfileImageName = "default_profile_icon.png";
public const string FavoriteListName = "Favorites";
private static readonly Lazy<Bitmap> DefaultProfileImageLazy = new Lazy<Bitmap>(BuildDefaultProfileImage);
public static Bitmap DefaultProfileImage => DefaultProfileImageLazy.Value;
public static string FavoriteListName = "Favorites";
private static Bitmap BuildDefaultProfileImage()
{
try
......
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
......@@ -12,6 +13,7 @@ using MySocialPortalLib.Service;
namespace MySocialPortalDesktop.Util
{
[SuppressMessage("ReSharper", "CA1031")]
public static class PersonDataGenerator
{
public static async Task<string> GetDefaultUserName(Person person)
......
......@@ -12,7 +12,6 @@ namespace MySocialPortalDesktop.ViewModels
{
private const int DefaultMaxHeight = 150;
private const int DefaultMaxWidth = 250;
private const int MaxDescriptionLenth = 240;
private bool _disposed = false;
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
......@@ -20,6 +21,9 @@ using ReactiveUI;
namespace MySocialPortalDesktop.ViewModels
{
[SuppressMessage("ReSharper", "CA1822")]
[SuppressMessage("ReSharper", "CA1303")]
[SuppressMessage("ReSharper", "CA1031")]
public class MainWindowViewModel : ViewModelBase
{
private const int DefaultMaxPosts = 50;
......@@ -28,11 +32,16 @@ namespace MySocialPortalDesktop.ViewModels
public PostTimelineViewModel PostTimelineViewModel { get; }
public ObservableCollection<string> UserLists { get; }
public MainWindowViewModel()
{
ImportedPosts = new List<Post>();
PostTimelineViewModel = DefaultValueService.GetDefaultHomePostTimelineViewModel(DefaultMaxPosts);
PeopleListViewModel = new PeopleListViewModel(PostTimelineViewModel);
UserLists = new ObservableCollection<string>();
var userLists = RepositoryFactory.Instance.ListsRepository.GetAllLists();
UserLists.AddRange(userLists);
}
public void DoExit()
......@@ -106,7 +115,6 @@ namespace MySocialPortalDesktop.ViewModels
private List<Post> ImportedPosts { get; }
private async Task<bool> ImportPeopleJsonToDatabase(string path)
{
if (path == null)
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using DynamicData;
using MySocialPortalDesktop.Factory;
......@@ -10,6 +11,8 @@ using MySocialPortalLib.Util;
namespace MySocialPortalDesktop.ViewModels
{
[SuppressMessage("ReSharper", "CA1303")]
[SuppressMessage("ReSharper", "CA1031")]
public class PostTimelineViewModel : ViewModelBase
{
private const int MaxPostsQuery = 20;
......@@ -60,7 +63,9 @@ namespace MySocialPortalDesktop.ViewModels
TwitterConnector.GetNewerHomeTimeline(10).ToList() :
TwitterConnector.GetNewerUserTimeline(CurrentPerson, MaxPostsQuery).ToList();
ProcessNewPosts(posts, true);
PostDatabaseMergingUtils.AddOrUpdateToRepository(RepositoryFactory.Instance.AllPostsRepository, posts);
AddPostsToViewList(posts, true);
}
catch (Exception e)
{
......@@ -75,7 +80,9 @@ namespace MySocialPortalDesktop.ViewModels
var posts = CurrentPerson == null ?
TwitterConnector.GetOlderHomeTimeline(10).ToList() :
TwitterConnector.GetOlderUserTimeline(CurrentPerson, MaxPostsQuery).ToList();
ProcessNewPosts(posts, false);
PostDatabaseMergingUtils.AddOrUpdateToRepository(RepositoryFactory.Instance.AllPostsRepository, posts);
AddPostsToViewList(posts, false);
}
catch (Exception e)
{
......@@ -83,6 +90,22 @@ namespace MySocialPortalDesktop.ViewModels
}
}
public async void RefreshList(string listName)
{
if (string.IsNullOrWhiteSpace(listName))
{
return;
}
Console.WriteLine($"Attempting to pull updates for users in list: {listName}");
var newPosts = await UserListPostUpdaterFactory.GetDefault().Update(listName).ConfigureAwait(false);
Console.WriteLine($"# posts pulled on updates: {newPosts?.Count}");
AddPostsToViewList(newPosts, false);
}
private Person CurrentPerson { get; set; }
private TwitterConnector TwitterConnector { get; }
......@@ -97,7 +120,7 @@ namespace MySocialPortalDesktop.ViewModels
postsList.ForEach(p => PostViewModels.Add(new PostViewModel(p)));
}
private void ProcessNewPosts(List<Post> posts, bool insertTop)
private void AddPostsToViewList(List<Post> posts, bool insertTop)
{
if (posts.Count == 0)
{
......@@ -105,9 +128,6 @@ namespace MySocialPortalDesktop.ViewModels
return;
}
var filteredPosts = posts.FindAll(p =>
RepositoryFactory.Instance.AllPostsRepository.GetByOriginalNetworkId(p.OriginalLinkUrlOrId) == null);
RepositoryFactory.Instance.AllPostsRepository.AddPosts(filteredPosts);
if (insertTop)
{
posts.Sort(new PostComparison(true));
......
......@@ -5,6 +5,7 @@ using MySocialPortalLib.Model;
using MySocialPortalLib.Service;
using ReactiveUI;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using LinqToTwitter.Net;
......@@ -13,6 +14,7 @@ using MySocialPortalDesktop.Util;
namespace MySocialPortalDesktop.ViewModels
{
[SuppressMessage("ReSharper", "CA1031")]
public class PostViewModel : ViewModelBase
{
private string _body;
......@@ -60,6 +62,10 @@ namespace MySocialPortalDesktop.ViewModels
public PostViewModel(Post post)
{
if (post == null)
{
throw new ArgumentNullException(nameof(post));
}
Links = new ObservableCollection<ExternalLinkViewModel>();
Post = post;
Title = "Unknown Author";
......
......@@ -35,6 +35,16 @@
<TextBlock Text="Load Latest Posts"></TextBlock>
</ToolTip.Tip>
</RibbonButton>
<RibbonComboButton Name="RefreshPeopleInList" IconPath="/Assets/RibbonIcons/refresh.png" Items="{Binding UserLists}">
<RibbonComboButton.DataTemplates>
<DataTemplate>
<TextBlock Text="{Binding}" Margin="3"/>
</DataTemplate>
</RibbonComboButton.DataTemplates>
<ToolTip.Tip>
<TextBlock Text="Refresh Posts from users in list"/>
</ToolTip.Tip>
</RibbonComboButton>
</StackPanel>
</RibbonTabGroup>
<RibbonTabGroup Text="Import">
......
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Ribbon;
using Avalonia.Markup.Xaml;
using MySocialPortalDesktop.Factory;
using MySocialPortalDesktop.ViewModels;
namespace MySocialPortalDesktop.Views
{
public class MainWindow : Window
{
private RibbonComboButton _refreshUsersFromListCombo;
public MainWindow()
{
InitializeComponent();
InitializeCallbacks();
}
private void InitializeComponent()
......@@ -18,10 +22,28 @@ namespace MySocialPortalDesktop.Views
AvaloniaXamlLoader.Load(this);
}
private void InitializeCallbacks()
{
_refreshUsersFromListCombo = this.FindControl<RibbonComboButton>("RefreshPeopleInList");
_refreshUsersFromListCombo.SelectionChanged += RefreshSelectedUserList;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
RepositoryFactory.Instance.Dispose();
}
private void RefreshSelectedUserList(object sender, SelectionChangedEventArgs args)
{
if (args.AddedItems.Count == 0)
{
return;
}
var listName = args.AddedItems[0] as string;
(DataContext as MainWindowViewModel)?.PostTimelineViewModel?.RefreshList(listName);
}
}
}
\ No newline at end of file
......@@ -53,7 +53,7 @@ namespace MySocialPortalLib.Converter
if (tweet?.Entities?.HashTagEntities?.Count > 0)
{
post.Tags.AddRange(tweet.Entities.HashTagEntities.Select(h => h.Text));
post.AddTags(tweet.Entities.HashTagEntities.Select(h => h.Text));
}
if (tweet?.Entities?.UserMentionEntities?.Count > 0)
......
using MySocialPortalLib.Service;
namespace MySocialPortalDesktop.Factory
{
public interface ISocialMediaConnectorFactory
{
ISocialMediaConnector GetConnectorByNetworkName(string name);
}
}
\ No newline at end of file
using System;
using System.Diagnostics.CodeAnalysis;
using MySocialPortalLib.Model;
using MySocialPortalLib.Service;
using MySocialPortalLib.Service.SocialMediaConnectors;
namespace MySocialPortalDesktop.Factory
{
[SuppressMessage("ReSharper", "CA1822")]
public static class SocialMediaConnectorFactory
public class SocialMediaConnectorFactory : ISocialMediaConnectorFactory
{
public static TwitterConnector GetNewTwitterConnector()
{
return new TwitterConnector(RepositoryFactory.Instance.MainPeopleRepository,
ServiceFactory.BuildNewTimelineManagementService(StandardSocialNetworkNames.Twitter));
}
public ISocialMediaConnector GetConnectorByNetworkName(string name)
{
switch (name)
{
case StandardSocialNetworkNames.Twitter:
return GetNewTwitterConnector();
default:
return null;
}
}
}
}
\ No newline at end of file
using MySocialPortalLib.Util;
namespace MySocialPortalDesktop.Factory
{
public static class UserListPostUpdaterFactory
{
public const int DefaultMaxPosts = 25;
public static UserListPostUpdater GetDefault(int maxPosts = DefaultMaxPosts)
{
var repoFactory = RepositoryFactory.Instance;
return new UserListPostUpdater(new SocialMediaConnectorFactory(),
repoFactory.AllPostsRepository, repoFactory.MainPeopleRepository, repoFactory.ListsRepository,
maxPosts);
}
}
}
\ No newline at end of file
......@@ -4,26 +4,23 @@ namespace MySocialPortalLib.Model
{
public class EventData
{
public static readonly DateTimeOffset DateNotSetValue = DateTimeOffset.MinValue;
public string Id { get; set; }
public string Title { get; set; }
public long StartTimestamp { get; set; }
public long StopTimestamp { get; set; }
public Place Place { get; set; }
public DateTimeOffset StartTime => DateTimeOffset.UnixEpoch.AddSeconds(StartTimestamp);
public DateTimeOffset StartTime { get; set; }
public DateTimeOffset StopTime => DateTimeOffset.UnixEpoch.AddSeconds(StopTimestamp);
public DateTimeOffset StopTime { get; set; }
public EventData()
{
Id = Guid.NewGuid().ToString();
Title = "";
StartTimestamp = 0;
StopTimestamp = StartTimestamp;
StartTime = DateNotSetValue;
StopTime = DateNotSetValue;
Place = new Place();
}
......@@ -37,8 +34,8 @@ namespace MySocialPortalLib.Model
return Id == other.Id
&& Title == other.Title
&& StartTimestamp == other.StartTimestamp
&& StopTimestamp == other.StopTimestamp
&& StartTime == other.StartTime
&& StartTime == other.StartTime
&& Equals(Place, other.Place);
}
......
......@@ -6,9 +6,7 @@ namespace MySocialPortalLib.Model
[SuppressMessage("ReSharper", "CA1056")]
public class MediaData
{
public DateTimeOffset CreationDateTime => DateTimeOffset.UnixEpoch.AddSeconds(CreationTimestamp);
public long CreationTimestamp { get; set; }
public DateTimeOffset CreationDateTime { get; set; }
public string Description { get; set; }
......@@ -24,13 +22,13 @@ namespace MySocialPortalLib.Model
public MediaData()
{
CreationTimestamp = 0L;
Id = Guid.NewGuid().ToString();
Description = "";
LocalPath = "";
MediaType = "";
Title = "";
OriginalUrl = "";
CreationDateTime = DateTimeOffset.UnixEpoch;
}
public bool AllEquals(MediaData other)
......@@ -40,7 +38,7 @@ namespace MySocialPortalLib.Model
return false;
}
return CreationTimestamp == other.CreationTimestamp
return CreationDateTime == other.CreationDateTime
&& Id == other.Id
&& Title == other.Title
&& Description == other.Description
......
......@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using MySocialPortalLib.Util;
namespace MySocialPortalLib.Model
{
......@@ -89,6 +90,13 @@ namespace MySocialPortalLib.Model
&& UserId == other.UserId;
}
public void AddTags(IEnumerable<string> newTags)
{
var tags = new List<string>(Tags);
PostMergingUtils.MergeStringCollections(tags, newTags);
Tags.Clear();
Tags.AddRange(tags.Select(t => t.ToUpperInvariant()));
}
protected bool Equals(Post other)
{
......
namespace MySocialPortalLib
{
public class PollOption
{
}
}
\ No newline at end of file
......@@ -6,18 +6,19 @@ namespace MySocialPortalLib.Repository
{
public interface IPostsRepository
{
void AddPost(Post post);
void AddOrUpdate(Post post);
void AddPosts(IEnumerable<Post> posts);
Post? GetById(string id);
Post? GetByOriginalNetworkId(string id);
Post? GetByOriginalNetworkId(string network, string id);
IEnumerable<Post> GetPosts(int maxPosts, string personId = "", string networkName = "");
IEnumerable<Post> GetPosts(DateTimeOffset newestPostTime, int maxPosts, bool inclusive, string personId = "", string networkName = "");
IEnumerable<Post> GetPosts(DateTimeOffset oldestPostTime, DateTimeOffset newestPostTime, int maxPosts, bool inclusive, string personId = "", string networkName = "");
}
}
\ No newline at end of file
......@@ -52,9 +52,9 @@ namespace MySocialPortalLib.Repository
}
public void AddPost(Post post)
public void AddOrUpdate(Post post)
{
_postsRepository.Insert(post, _postsCollectionName);
_postsRepository.Upsert(post, _postsCollectionName);
}
public void AddPosts(IEnumerable<Post> posts)
......@@ -79,7 +79,7 @@ namespace MySocialPortalLib.Repository
.FirstOrDefault();
}
public Post GetByOriginalNetworkId(string id)
public Post GetByOriginalNetworkId(string network, string id)
{
if (string.IsNullOrWhiteSpace(id))
{
......@@ -87,7 +87,7 @@ namespace MySocialPortalLib.Repository
}
return _postsRepository.Query<Post>(_postsCollectionName)
.Where(p => p.OriginalLinkUrlOrId == id)
.Where(p => p.OriginalLinkUrlOrId == id && p.OriginalSocialMediaSystem == network)
.FirstOrDefault();
}
......
......@@ -14,5 +14,6 @@ namespace MySocialPortalLib.Service
IEnumerable<Post> GetOlderUserTimeline(Person person, int maxPosts);
bool UserOnSocialNetwork(Person person);
}
}
\ No newline at end of file
......@@ -76,6 +76,7 @@ namespace MySocialPortalLib.Service.SocialMediaConnectors
var newInterval = TimelineManager.GetNextSamplingInterval(twitterId, EarliestTweetValue, LatestTweetValue);
return PullUserTweets(twitterId, newInterval, maxPosts);
}
public IEnumerable<Post> GetOlderUserTimeline(Person person, int maxPosts)
{
var twitterId = "";
......
using System.Collections.Generic;
using MySocialPortalLib.Model;
using MySocialPortalLib.Repository;
namespace MySocialPortalLib.Util
{
public static class PostDatabaseMergingUtils
{
public static (int added, int updated) AddOrUpdateToRepository(IPostsRepository postsRepository, IEnumerable<Post> posts)
{
if (postsRepository == null || posts == null)
{
return (0, 0);
}
var addedPosts = 0;
var updatePosts = 0;
foreach (var post in posts)
{
var oldPost = postsRepository.GetById(post.Id) ??
postsRepository.GetByOriginalNetworkId(post.OriginalSocialMediaSystem,
post.OriginalLinkUrlOrId);
if (oldPost == null)
{
postsRepository.AddOrUpdate(post);
addedPosts++;
}
else
{
post.Id = oldPost.Id;
oldPost.Merge(post);
postsRepository.AddOrUpdate(oldPost);
updatePosts++;
}
}
return (addedPosts, updatePosts);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using MySocialPortalLib.Model;
namespace MySocialPortalLib.Util
{
public static class PostMergingUtils
{
public static void Merge(this Post post1, Post post2)
{
if (!IsReallySamePost(post1, post2))
{
return;
}
if (!string.IsNullOrWhiteSpace(post2.Body))
{
post1.Body = post2.Body;
}
post1.Events.AddIfNew(post2.Events);
post1.Links.AddIfNew(post2.Links);
post1.Media.AddIfNew(post2.Media);
post1.Places.AddIfNew(post2.Places);
post1.Polls.AddIfNew(post2.Polls);
MergeStringCollections(post1.Tags, post2.Tags);
MergeStringCollections(post1.TaggedUsers, post2.TaggedUsers);
post1.RelatedPosts.AddIfNew(post2.RelatedPosts);
post1.UserId = post2.UserId;
post1.PostDateTime = post2.PostDateTime;
post1.OriginalSocialMediaSystem = post2.OriginalSocialMediaSystem;
post1.OriginalLinkUrlOrId = post2.OriginalLinkUrlOrId;
post1.Title = post2.Title;
}
public static void AddIfNew(this List<EventData> currentEvents, List<EventData> newEvents)
{
if (currentEvents == null || newEvents == null)
{
return;
}
if (!currentEvents.Any())
{
currentEvents.AddRange(newEvents);
return;
}
foreach(var evt in newEvents)
{
var sameOldEvent = currentEvents.FirstOrDefault(e1 => e1.Id == evt.Id);
if (sameOldEvent != null)
{
sameOldEvent.Merge(evt);
}
else
{
currentEvents.Add(evt);
}
}
}
public static void AddIfNew(this List<ExternalLink> currentLinks, List<ExternalLink> newLinks)
{
if (currentLinks == null || newLinks == null)
{
return;
}
if (!currentLinks.Any())
{
currentLinks.AddRange(newLinks);
return;
}
foreach (var link in newLinks)
{
var oldLink = currentLinks.FirstOrDefault(l => l.Url == link.Url);
if (oldLink == null)
{
currentLinks.Add(link);
}
else
{
oldLink.Merge(link);
}
}
}
public static void AddIfNew(this List<MediaData> currentMedia, List<MediaData> newMedia)
{
if (currentMedia == null || newMedia == null)
{
return;
}
if (!currentMedia.Any())
{
currentMedia.AddRange(newMedia);
return;
}
foreach (var media in newMedia)
{
var oldMedia = currentMedia.FirstOrDefault(m => m.Id == media.Id);
if (oldMedia == null)
{
currentMedia.Add(media);
}
else
{
oldMedia.Merge(media);
}
}
}
public static void AddIfNew(this List<Place> currentPlaces, List<Place> newPlaces)
{
if (currentPlaces == null || newPlaces == null)
{
return;
}
if (!currentPlaces.Any())
{
currentPlaces.AddRange(newPlaces);
return;
}
foreach (var place in newPlaces)
{
var oldPlace = currentPlaces.FirstOrDefault(p => p.Id == place.Id);
if (oldPlace == null)
{
currentPlaces.Add(place);
}
else
{
oldPlace.Merge(place);
}
}
}
public static void AddIfNew(this List<Poll> currentPolls, List<Poll> newPolls)
{
if (currentPolls == null || newPolls == null)
{
return;
}
if (!currentPolls.Any())
{
currentPolls.AddRange(newPolls);
return;
}
foreach (var poll in newPolls)
{
var oldPoll = currentPolls.FirstOrDefault(p => p.Id == poll.Id);
if (oldPoll == null)
{
currentPolls.Add(poll);
}
else
{
oldPoll.Merge(poll);
}
}
}
public static void AddIfNew(this List<PollOption> currentPollOptions, List<PollOption> newPollOptions)
{
if (currentPollOptions == null || newPollOptions == null)
{
return;
}
if (!currentPollOptions.Any())
{
currentPollOptions.AddRange(newPollOptions);
return;
}
foreach (var pollOption in newPollOptions)
{
var oldPollOption = currentPollOptions.FirstOrDefault(p => p.Text == pollOption.Text);
if (oldPollOption == null)
{
currentPollOptions.Add(pollOption);
}
else
{
oldPollOption.Vote = pollOption.Vote;
}
}
}
public static void Merge(this EventData event1, EventData event2)
{
if (event1 == null || event2 == null)
{
return;
}
event1.Title = event2.Title;
event1.StartTime = event2.StartTime;
event1.StopTime = event2.StopTime;
event1.Place.Merge(event2.Place);
}
public static void Merge(this ExternalLink link1, ExternalLink link2)
{
if (link1 == null || link2 == null)
{
return;
}
link1.Name = link2.Name;
link1.Source = link2.Source;
if (!string.IsNullOrWhiteSpace(link2.PostId))
{
link1.PostId = link2.PostId;
}
}
public static void Merge(this MediaData media1, MediaData media2)
{
if (media1 == null || media2 == null)
{
return;
}
media1.Description = media2.Description;
media1.Title = media2.Title;
media1.LocalPath = media2.LocalPath;
media1.MediaType = media2.MediaType;
media1.OriginalUrl = media2.OriginalUrl;
media1.CreationDateTime = media2.CreationDateTime;
}
public static void Merge(this Place place1, Place place2)
{
if (place1 == null || place2 == null)
{
return;
}
place1.Altitude = place2.Altitude;
place1.Latitude = place2.Latitude;
place1.Longitude = place2.Longitude;
place1.Name = place2.Name;
place1.Url = place2.Url;
place1.PhysicalAddress = place2.PhysicalAddress;
place1.HasLatLon = place2.HasLatLon;
}
public static void Merge(this Poll poll1, Poll poll2)
{
if (poll1 == null || poll2 == null)
{
return;
}
poll1.Question = poll2.Question;
poll1.Options.AddIfNew(poll2.Options);
}
public static void MergeStringCollections(List<string> list1, IEnumerable<string> list2)
{
if (list1 == null || list2 == null)
{
return;
}
if (!list1.Any())
{
list1.AddRange(list2);
return;
}
var unique = new HashSet<string>(list1.Select(i => i.ToUpperInvariant()));
foreach (var i in list2)
{
if (unique.Contains(i.ToUpperInvariant()))
{
continue;
}
list1.Add(i);
}
}
private static bool IsReallySamePost(Post post1, Post post2)
{
if (post1 == null || post2 == null)
{
return false;
}
if (string.Equals(post1.Id, post2.Id, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
if (string.Equals(post1.OriginalSocialMediaSystem,post2.OriginalSocialMediaSystem, StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
return string.Equals(post1.OriginalLinkUrlOrId, post2.OriginalLinkUrlOrId,
StringComparison.InvariantCultureIgnoreCase);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MySocialPortalDesktop.Factory;
using MySocialPortalLib.Model;
using MySocialPortalLib.Repository;
namespace MySocialPortalLib.Util
{
public class UserListPostUpdater
{
public ISocialMediaConnectorFactory ConnectorFactory { get; }
public int MaxPosts { get; }
public IPersonsRepository PersonsRepository { get; }
public IPostsRepository PostsRepository { get; }
public INamedListRepository UsersLists { get; }
public UserListPostUpdater(ISocialMediaConnectorFactory connectorFactory,
IPostsRepository postsRepository, IPersonsRepository personsRepository,
INamedListRepository usersLists,
int maxPosts)
{
ConnectorFactory = connectorFactory;
PersonsRepository = personsRepository;
PostsRepository = postsRepository;
UsersLists = usersLists;
MaxPosts = maxPosts;
}
public async Task<List<Post>> Update(string userListName)
{
var idsToProcess = UsersLists.GetAllIdsForList(userListName);
var newPosts = new List<Post>();
if (idsToProcess == null || idsToProcess.Count == 0)
{
return newPosts;
}
foreach (var id in idsToProcess)
{
var person = PersonsRepository.FindById(id);
if (person == null)
{
continue;
}
foreach (var sma in person.SocialMediaAccounts.Values)
{
var connector = ConnectorFactory.GetConnectorByNetworkName(sma.SocialMediaSystemName);
if (connector == null)
{
continue;
}
var posts = connector.GetNewerUserTimeline(person, MaxPosts);
foreach (var post in posts)
{
newPosts.Add(post);
var oldPost = PostsRepository.GetByOriginalNetworkId(post.OriginalSocialMediaSystem,
post.OriginalLinkUrlOrId);
if (oldPost == null)
{
PostsRepository.AddOrUpdate(post);
}
else
{
oldPost.Merge(post);
PostsRepository.AddOrUpdate(oldPost);
}
}
}
}
return newPosts;
}
}
}
\ No newline at end of file
......@@ -31,8 +31,8 @@ namespace MySocialPortalLibTest.Model
new EventData{
Place = new Place(),
Title = "Title 1",
StartTimestamp = 100,
StopTimestamp = 110
StartTime = DateTimeOffset.UnixEpoch.AddDays(100),
StopTime = DateTimeOffset.UnixEpoch.AddDays(110)
}
}
};
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.Json;
......@@ -29,7 +30,7 @@ namespace MySocialPortalLibTest.Model
new MediaData(){
Description = "Media description",
Title = "Media title",
CreationTimestamp = 1000,
CreationDateTime = DateTimeOffset.UnixEpoch.AddDays(1000),
LocalPath = "media/video/12456.mp4",
MediaType = "video/mp4",
OriginalUrl = "https://movies.com/preview1234.mp4"
......
......@@ -30,10 +30,24 @@ namespace MySocialPortalLibTest.Service
{
using var postDb = GetTempDb();
Assert.Equal(0, postDb.Count());
postDb.AddPost(new Post{Title = "Test Post1"});
postDb.AddOrUpdate(new Post{Title = "Test Post1"});
Assert.Equal(1, postDb.Count());
}
[Fact]
public void TestUpdate()
{
using var postDb = GetTempDb();
Assert.Equal(0, postDb.Count());
var post1 = new Post {Title = "Test Post1"};
var post2 = new Post {Id = post1.Id, Title = "Test Title 2"};
postDb.AddOrUpdate(post1);
postDb.AddOrUpdate(post2);
Assert.Equal(1, postDb.Count());
var pulledPost = postDb.GetById(post2.Id);
Assert.Equal(post2.Title, pulledPost.Title);
}
[Fact]
public void TestAddMultipleAndCount()
{
......@@ -121,7 +135,7 @@ namespace MySocialPortalLibTest.Service
{
UserId = userId
};
postDb.AddPost(post);
postDb.AddOrUpdate(post);
var postResults = postDb.GetPosts(personId: userId);
Assert.Equal(1, postResults.Count());
Assert.Equal(post, postResults.First());
......@@ -143,21 +157,21 @@ namespace MySocialPortalLibTest.Service
UserId = userId,
OriginalSocialMediaSystem = StandardSocialNetworkNames.Twitter
};
postDb.AddPost(post1);
postDb.AddOrUpdate(post1);
var post2 = new Post
{
UserId = userId,
OriginalSocialMediaSystem = StandardSocialNetworkNames.Diaspora
};
postDb.AddPost(post2);
postDb.AddOrUpdate(post2);
var post3 = new Post
{
UserId = Guid.NewGuid().ToString(),
OriginalSocialMediaSystem = StandardSocialNetworkNames.Diaspora
};
postDb.AddPost(post3);
postDb.AddOrUpdate(post3);
var postResults = postDb.GetPosts(personId: userId, networkName: StandardSocialNetworkNames.Diaspora);
Assert.Equal(1, postResults.Count());
Assert.Equal(post2, postResults.First());
......@@ -169,6 +183,42 @@ namespace MySocialPortalLibTest.Service
Assert.False(postResults.Any());
}
[Fact]
public void TestGetByOriginalNetworkId()
{
using var postDb = GetTempDb();
var postCount = 10;
var postLimit = 3;
var posts = GeneratePosts(postCount);
postDb.AddPosts(posts);
var networkIdCollision = "123456";
var otherNetworkName = "OtherNetwork";
var post1 = new Post
{
OriginalSocialMediaSystem = StandardSocialNetworkNames.Twitter,
OriginalLinkUrlOrId = networkIdCollision
};
postDb.AddOrUpdate(post1);
var post2 = new Post
{
OriginalSocialMediaSystem = StandardSocialNetworkNames.Diaspora,
OriginalLinkUrlOrId = "DA5794C2-DD24-4B71-803A-AA5E06EAFA91"
};
postDb.AddOrUpdate(post2);
var post3 = new Post
{
OriginalSocialMediaSystem = otherNetworkName,
OriginalLinkUrlOrId = networkIdCollision
};
postDb.AddOrUpdate(post3);
Assert.Equal(post1, postDb.GetByOriginalNetworkId(StandardSocialNetworkNames.Twitter, networkIdCollision));
Assert.Equal(post2, postDb.GetByOriginalNetworkId(StandardSocialNetworkNames.Diaspora, post2.OriginalLinkUrlOrId));
Assert.Equal(post3, postDb.GetByOriginalNetworkId(otherNetworkName, networkIdCollision));
Assert.Null(postDb.GetByOriginalNetworkId(StandardSocialNetworkNames.Diaspora, networkIdCollision));
}
[Fact]
public void TestPagingFlow()
{
......
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MySocialPortalLib.Model;
using MySocialPortalLib.Repository;
using MySocialPortalLib.Util;
using Xunit;
namespace MySocialPortalLibTest.Util
{
public class PostDatabaseMergingUtilsTest
{
[Fact]
public void TestAddOrUpdateToRepository()
{
var postDb = GetTempPostDb();
var post1 = new Post {Title = "Post 1"};
postDb.AddOrUpdate(post1);
var post2 = new Post {Title = "Post 2", OriginalSocialMediaSystem = "SM1", OriginalLinkUrlOrId = "SM1Post2"};
postDb.AddOrUpdate(post2);
postDb.AddOrUpdate(new Post());
postDb.AddOrUpdate(new Post());
postDb.AddOrUpdate(new Post());
postDb.AddOrUpdate(new Post());
var updatePost1 = new Post {Id = post1.Id, Title = "New Title Post 1"};
var updatePost2 = new Post
{
Title = "New Title Post2",
OriginalSocialMediaSystem = post2.OriginalSocialMediaSystem,
OriginalLinkUrlOrId = post2.OriginalLinkUrlOrId
};
var newPosts = new List<Post> {updatePost2, new Post(), updatePost1, new Post(), new Post()};
PostDatabaseMergingUtils.AddOrUpdateToRepository(postDb, newPosts);
var allPosts = postDb.GetPosts(10000);
Assert.Equal(9, allPosts.Count());
var post1Latest = postDb.GetById(post1.Id);
Assert.Equal(updatePost1.Title, post1Latest.Title);
var post2Latest = postDb.GetById(post2.Id);
Assert.Equal(updatePost2.Title, post2Latest.Title);
}
private PostsLiteDbRepository GetTempPostDb()
{
return new PostsLiteDbRepository(new FileStream(Path.GetTempFileName() + "posts.db", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None, 4096,
FileOptions.RandomAccess | FileOptions.DeleteOnClose));
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LiteDB;
using MySocialPortalDesktop.Factory;
using MySocialPortalLib.Model;
using MySocialPortalLib.Repository;
using MySocialPortalLib.Service;
using MySocialPortalLib.Util;
using Xunit;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace MySocialPortalLibTest.Util
{
public class TestUserListPostUpdater
{
private const string UserListName1 = "userlist1";
[Fact]
public void TestUpdating()
{
var updater = GetUpdater();
Populate(updater);
updater.Update(UserListName1);
var posts = updater.PostsRepository.GetPosts(100000).ToList();
Assert.Equal(40, posts.Count);
(updater.PostsRepository as PostsLiteDbRepository)?.Dispose();
(updater.PersonsRepository as PersonsLiteDbRepository)?.Dispose();
}
private UserListPostUpdater GetUpdater()
{
var updater = new UserListPostUpdater(new SocialMediaConnectorFactoryStub(),
GetTempPostDb(), GetTempPersonDb(),
GetNamedListDb(), 10);
return updater;
}
private PostsLiteDbRepository GetTempPostDb()
{
return new PostsLiteDbRepository(new FileStream(Path.GetTempFileName() + "posts.db", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None, 4096,
FileOptions.RandomAccess | FileOptions.DeleteOnClose));
}
private PersonsLiteDbRepository GetTempPersonDb()
{
return new PersonsLiteDbRepository(new FileStream(Path.GetTempFileName() + "persons.db", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None, 4096,
FileOptions.RandomAccess | FileOptions.DeleteOnClose));
}
private NamedListLiteDbRepository GetNamedListDb()
{
var fileStream = new FileStream(Path.GetTempFileName() + "lists.db", FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None, 4096,
FileOptions.RandomAccess | FileOptions.DeleteOnClose);
var repo = new LiteRepository(fileStream);
var listRepo = new NamedListLiteDbRepository(repo);
return listRepo;
}
private void Populate(UserListPostUpdater updater)
{
var person1 = new Person
{
Name = "Person1"
};
person1.SocialMediaAccounts[StandardSocialNetworkNames.Diaspora] = new SocialMediaAccountData
{
Id = "1234",
ProfileId = "UserProfile1234",
SocialMediaSystemName = StandardSocialNetworkNames.Diaspora
};
person1.SocialMediaAccounts[SocialMediaConnectorFactoryStub.SocialMediaNames[0]] = new SocialMediaAccountData
{
Id = "1234",
ProfileId = "UserProfile1234",
SocialMediaSystemName = SocialMediaConnectorFactoryStub.SocialMediaNames[0]
};
person1.SocialMediaAccounts[SocialMediaConnectorFactoryStub.SocialMediaNames[1]] = new SocialMediaAccountData
{
Id = "1234",
ProfileId = "UserProfile1234",
SocialMediaSystemName = SocialMediaConnectorFactoryStub.SocialMediaNames[1]
};
updater.PersonsRepository.AddPerson(person1);
updater.UsersLists.Add(person1.Id, UserListName1);
var person2 = new Person
{
Name = "Person2"
};
person2.SocialMediaAccounts[StandardSocialNetworkNames.Diaspora] = new SocialMediaAccountData
{
Id = "2",
ProfileId = "UserProfile2",
SocialMediaSystemName = StandardSocialNetworkNames.Diaspora
};
person2.SocialMediaAccounts[SocialMediaConnectorFactoryStub.SocialMediaNames[0]] = new SocialMediaAccountData
{
Id = "2",
ProfileId = "UserProfile2",
SocialMediaSystemName = SocialMediaConnectorFactoryStub.SocialMediaNames[0]
};
person2.SocialMediaAccounts[SocialMediaConnectorFactoryStub.SocialMediaNames[1]] = new SocialMediaAccountData
{
Id = "2",
ProfileId = "UserProfile2",
SocialMediaSystemName = SocialMediaConnectorFactoryStub.SocialMediaNames[1]
};
updater.PersonsRepository.AddPerson(person2);
updater.UsersLists.Add(person2.Id, "OtherUserList");
var person3 = new Person
{
Name = "Person3"
};
person3.SocialMediaAccounts[StandardSocialNetworkNames.Diaspora] = new SocialMediaAccountData
{
Id = "3",
ProfileId = "UserProfile3",
SocialMediaSystemName = StandardSocialNetworkNames.Diaspora
};
updater.PersonsRepository.AddPerson(person3);
updater.UsersLists.Add(person3.Id, UserListName1);
var person4 = new Person
{
Name = "Person4"
};
person4.SocialMediaAccounts[StandardSocialNetworkNames.Diaspora] = new SocialMediaAccountData
{
Id = "4",
ProfileId = "UserProfile4",
SocialMediaSystemName = StandardSocialNetworkNames.Diaspora
};
person4.SocialMediaAccounts[SocialMediaConnectorFactoryStub.SocialMediaNames[0]] = new SocialMediaAccountData
{
Id = "4",
ProfileId = "UserProfil4",
SocialMediaSystemName = SocialMediaConnectorFactoryStub.SocialMediaNames[0]
};
person4.SocialMediaAccounts[SocialMediaConnectorFactoryStub.SocialMediaNames[1]] = new SocialMediaAccountData
{
Id = "4",
ProfileId = "UserProfile4",
SocialMediaSystemName = SocialMediaConnectorFactoryStub.SocialMediaNames[0]
};
updater.PersonsRepository.AddPerson(person4);
updater.UsersLists.Add(person4.Id, UserListName1);
}
class SocialMediaConnectorFactoryStub : ISocialMediaConnectorFactory
{
public static readonly List<string> SocialMediaNames = new List<string> {"SocialNetwork1", "SocialNetwork2"};
public ISocialMediaConnector GetConnectorByNetworkName(string name)
{
if (SocialMediaNames.Contains(name))
{
return new SocialMediaConnectorStub(name);
}
return null;
}
}
class SocialMediaConnectorStub : ISocialMediaConnector
{
public string NetworkName { get; set; }
public SocialMediaConnectorStub(string networkName)
{
NetworkName = networkName;
}
public IEnumerable<Post> GetNewerHomeTimeline(int maxPosts)
{
throw new System.NotImplementedException();
}
public IEnumerable<Post> GetOlderHomeTimeline(int maxPosts)
{
throw new System.NotImplementedException();
}
public IEnumerable<Post> GetNewerUserTimeline(Person person, int maxPosts)
{
var newPosts = new List<Post>(maxPosts);
for (var i = 0; i < maxPosts; i++)
{
var post = new Post
{
Body = $"New Post at {DateTimeOffset.Now} for {person.Name}",
UserId = person.Id,
OriginalSocialMediaSystem = NetworkName,
OriginalLinkUrlOrId = Guid.NewGuid().ToString()
};
newPosts.Add(post);
}
return newPosts;
}
public IEnumerable<Post> GetOlderUserTimeline(Person person, int maxPosts)
{
throw new System.NotImplementedException();
}
public bool UserOnSocialNetwork(Person person)
{
throw new System.NotImplementedException();
}
}
}
}
\ No newline at end of file