Merge pull request #2556 from tidusjar/feature/vote

Feature/vote
This commit is contained in:
Jamie 2018-10-04 13:44:08 +01:00 committed by GitHub
commit b4a54839ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 2294 additions and 42 deletions

View file

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ombi.Core.Models;
using Ombi.Core.Models.UI;
using Ombi.Store.Entities;
namespace Ombi.Core.Engine
{
public interface IVoteEngine
{
Task<VoteEngineResult> DownVote(int requestId, RequestType requestType);
Task<Votes> GetVoteForUser(int requestId, string userId);
IQueryable<Votes> GetVotes(int requestId, RequestType requestType);
Task RemoveCurrentVote(Votes currentVote);
Task<VoteEngineResult> UpVote(int requestId, RequestType requestType);
Task<List<VoteViewModel>> GetMovieViewModel();
}
}

View file

@ -7,11 +7,8 @@ using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Entities;
using Microsoft.AspNetCore.Identity;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Helpers;
namespace Ombi.Core.Engine.Interfaces
{

View file

@ -61,7 +61,7 @@ namespace Ombi.Core.Engine
{
continue;
}
retVal.Add(await ProcessResult(tvMazeSearch));
retVal.Add(ProcessResult(tvMazeSearch));
}
return retVal;
}
@ -123,7 +123,7 @@ namespace Ombi.Core.Engine
public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
{
var result = await Cache.GetOrAdd(CacheKeys.PopularTv, async () => await TraktApi.GetPopularShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
var processed = ProcessResults(result);
return processed;
}
@ -131,35 +131,35 @@ namespace Ombi.Core.Engine
{
var result = await Cache.GetOrAdd(CacheKeys.AnticipatedTv, async () => await TraktApi.GetAnticipatedShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
var processed = ProcessResults(result);
return processed;
}
public async Task<IEnumerable<SearchTvShowViewModel>> MostWatches()
{
var result = await Cache.GetOrAdd(CacheKeys.MostWatchesTv, async () => await TraktApi.GetMostWatchesShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
var processed = ProcessResults(result);
return processed;
}
public async Task<IEnumerable<SearchTvShowViewModel>> Trending()
{
var result = await Cache.GetOrAdd(CacheKeys.TrendingTv, async () => await TraktApi.GetTrendingShows(), DateTime.Now.AddHours(12));
var processed = await ProcessResults(result);
var processed = ProcessResults(result);
return processed;
}
private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items)
private IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
{
var retVal = new List<SearchTvShowViewModel>();
foreach (var tvMazeSearch in items)
{
retVal.Add(await ProcessResult(tvMazeSearch));
retVal.Add(ProcessResult(tvMazeSearch));
}
return retVal;
}
private async Task<SearchTvShowViewModel> ProcessResult<T>(T tvMazeSearch)
private SearchTvShowViewModel ProcessResult<T>(T tvMazeSearch)
{
return Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
}

View file

@ -0,0 +1,254 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ombi.Core.Authentication;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Models;
using Ombi.Core.Models.UI;
using Ombi.Core.Rule.Interfaces;
using Ombi.Core.Settings;
using Ombi.Schedule.Jobs.Ombi;
using Ombi.Settings.Settings.Models;
using Ombi.Store.Entities;
using Ombi.Store.Repository;
namespace Ombi.Core.Engine
{
public class VoteEngine : BaseEngine, IVoteEngine
{
public VoteEngine(IRepository<Votes> votes, IPrincipal user, OmbiUserManager um, IRuleEvaluator r, ISettingsService<VoteSettings> voteSettings,
IMusicRequestEngine musicRequestEngine, ITvRequestEngine tvRequestEngine, IMovieRequestEngine movieRequestEngine) : base(user, um, r)
{
_voteRepository = votes;
_voteSettings = voteSettings;
_movieRequestEngine = movieRequestEngine;
_musicRequestEngine = musicRequestEngine;
_tvRequestEngine = tvRequestEngine;
}
private readonly IRepository<Votes> _voteRepository;
private readonly ISettingsService<VoteSettings> _voteSettings;
private readonly IMusicRequestEngine _musicRequestEngine;
private readonly ITvRequestEngine _tvRequestEngine;
private readonly IMovieRequestEngine _movieRequestEngine;
public async Task<List<VoteViewModel>> GetMovieViewModel()
{
var vm = new List<VoteViewModel>();
var movieRequests = await _movieRequestEngine.GetRequests();
var tvRequestsTask = _tvRequestEngine.GetRequests();
var musicRequestsTask = _musicRequestEngine.GetRequests();
var user = await GetUser();
foreach (var r in movieRequests)
{
if (r.Available || r.Approved || (r.Denied ?? false))
{
continue;
}
// Make model
var votes = GetVotes(r.Id, RequestType.Movie);
var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
vm.Add(new VoteViewModel
{
Upvotes = upVotes,
Downvotes = downVotes,
RequestId = r.Id,
RequestType = RequestType.Movie,
Title = r.Title,
Image = $"https://image.tmdb.org/t/p/w500/{r.PosterPath}",
Background = $"https://image.tmdb.org/t/p/w1280{r.Background}",
Description = r.Overview,
AlreadyVoted = myVote != null,
MyVote = myVote?.VoteType ?? VoteType.Downvote
});
}
foreach (var r in await musicRequestsTask)
{
if (r.Available || r.Approved || (r.Denied ?? false))
{
continue;
}
// Make model
var votes = GetVotes(r.Id, RequestType.Album);
var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
vm.Add(new VoteViewModel
{
Upvotes = upVotes,
Downvotes = downVotes,
RequestId = r.Id,
RequestType = RequestType.Album,
Title = r.Title,
Image = r.Cover,
Background = r.Cover,
Description = r.ArtistName,
AlreadyVoted = myVote != null,
MyVote = myVote?.VoteType ?? VoteType.Downvote
});
}
foreach (var r in await tvRequestsTask)
{
foreach (var childRequests in r.ChildRequests)
{
var finalsb = new StringBuilder();
if (childRequests.Available || childRequests.Approved || (childRequests.Denied ?? false))
{
continue;
}
var votes = GetVotes(childRequests.Id, RequestType.TvShow);
// Make model
var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync();
var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted);
foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber))
{
var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList();
var episodeString = NewsletterJob.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber));
finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}");
finalsb.Append("<br />");
}
vm.Add(new VoteViewModel
{
Upvotes = upVotes,
Downvotes = downVotes,
RequestId = childRequests.Id,
RequestType = RequestType.TvShow,
Title = r.Title,
Image = r.PosterPath,
Background = r.Background,
Description = finalsb.ToString(),
AlreadyVoted = myVote != null,
MyVote = myVote?.VoteType ?? VoteType.Downvote
});
}
}
return vm;
}
public IQueryable<Votes> GetVotes(int requestId, RequestType requestType)
{
return _voteRepository.GetAll().Where(x => x.RequestType == requestType && requestId == x.RequestId);
}
public Task<Votes> GetVoteForUser(int requestId, string userId)
{
return _voteRepository.GetAll().FirstOrDefaultAsync(x => x.RequestId == requestId && x.UserId == userId);
}
public async Task<VoteEngineResult> UpVote(int requestId, RequestType requestType)
{
// How many votes does this have?!
var currentVotes = GetVotes(requestId, requestType);
var voteSettings = await _voteSettings.GetSettingsAsync();
// Does this user have a downvote? If so we should revert it and make it an upvote
var user = await GetUser();
var currentVote = await GetVoteForUser(requestId, user.Id);
if (currentVote != null && currentVote.VoteType == VoteType.Upvote)
{
return new VoteEngineResult { ErrorMessage = "You have already voted!" };
}
await RemoveCurrentVote(currentVote);
await _movieRequestEngine.SubscribeToRequest(requestId, requestType);
await _voteRepository.Add(new Votes
{
Date = DateTime.UtcNow,
RequestId = requestId,
RequestType = requestType,
UserId = user.Id,
VoteType = VoteType.Upvote
});
var upVotes = await currentVotes.Where(x => x.VoteType == VoteType.Upvote).CountAsync();
var downVotes = -(await currentVotes.Where(x => x.VoteType == VoteType.Downvote).CountAsync());
var totalVotes = upVotes + downVotes;
RequestEngineResult result = null;
switch (requestType)
{
case RequestType.TvShow:
if (totalVotes >= voteSettings.TvShowVoteMax)
{
result = await _tvRequestEngine.ApproveChildRequest(requestId);
}
break;
case RequestType.Movie:
if (totalVotes >= voteSettings.MovieVoteMax)
{
result = await _movieRequestEngine.ApproveMovieById(requestId);
}
break;
case RequestType.Album:
if (totalVotes >= voteSettings.MusicVoteMax)
{
result = await _musicRequestEngine.ApproveAlbumById(requestId);
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(requestType), requestType, null);
}
if (result != null && !result.Result)
{
return new VoteEngineResult
{
ErrorMessage = "Voted succesfully but could not approve movie!"
};
}
return new VoteEngineResult
{
Result = true
};
}
public async Task<VoteEngineResult> DownVote(int requestId, RequestType requestType)
{
var user = await GetUser();
var currentVote = await GetVoteForUser(requestId, user.Id);
if (currentVote != null && currentVote.VoteType == VoteType.Downvote)
{
return new VoteEngineResult { ErrorMessage = "You have already voted!" };
}
await RemoveCurrentVote(currentVote);
await _movieRequestEngine.UnSubscribeRequest(requestId, requestType);
await _voteRepository.Add(new Votes
{
Date = DateTime.UtcNow,
RequestId = requestId,
RequestType = requestType,
UserId = user.Id,
VoteType = VoteType.Downvote
});
return new VoteEngineResult
{
Result = true
};
}
public async Task RemoveCurrentVote(Votes currentVote)
{
if (currentVote != null)
{
await _voteRepository.Delete(currentVote);
}
}
}
}

View file

@ -0,0 +1,18 @@
using Ombi.Store.Entities;
namespace Ombi.Core.Models.UI
{
public class VoteViewModel
{
public int RequestId { get; set; }
public RequestType RequestType { get; set; }
public string Image { get; set; }
public string Background { get; set; }
public int Upvotes { get; set; }
public int Downvotes { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool AlreadyVoted { get; set; }
public VoteType MyVote { get; set; }
}
}

View file

@ -0,0 +1,10 @@
namespace Ombi.Core.Models
{
public class VoteEngineResult
{
public bool Result { get; set; }
public string Message { get; set; }
public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
public string ErrorMessage { get; set; }
}
}