Rules changes and rework

pull/1425/head
Jamie.Rees 7 years ago
parent d5ec429893
commit 9f4a8902f9

@ -0,0 +1,22 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Moq;
namespace Ombi.Core.Tests
{
public static class DbHelper
{
public static DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
return dbSet.Object;
}
}
}

@ -0,0 +1,56 @@
using Moq;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Rules.Search;
using Ombi.Store.Context;
using Ombi.Store.Entities;
using Xunit;
namespace Ombi.Core.Tests.Rule.Search
{
public class RadarrCacheRuleTests
{
public RadarrCacheRuleTests()
{
ContextMock = new Mock<IOmbiContext>();
Rule = new RadarrCacheRule(ContextMock.Object);
}
private RadarrCacheRule Rule { get; }
private Mock<IOmbiContext> ContextMock { get; }
[Fact]
public void Should_ReturnApproved_WhenMovieIsInRadarr()
{
var list = DbHelper.GetQueryableMockDbSet(new RadarrCache
{
TheMovieDbId = 123
});
ContextMock.Setup(x => x.RadarrCache).Returns(list);
var request = new SearchMovieViewModel { Id = 123 };
var result = Rule.Execute(request);
Assert.Equal(result.Success, true);
Assert.Equal(request.Approved, true);
}
[Fact]
public void Should_ReturnNotApproved_WhenMovieIsNotInRadarr()
{
var list = DbHelper.GetQueryableMockDbSet(new RadarrCache
{
TheMovieDbId = 000012
});
ContextMock.Setup(x => x.RadarrCache).Returns(list);
var request = new SearchMovieViewModel { Id = 123 };
var result = Rule.Execute(request);
Assert.Equal(result.Success, true);
Assert.Equal(request.Approved, false);
}
}
}

@ -6,6 +6,7 @@ using Ombi.Store.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using Ombi.Core.Models.Search;
namespace Ombi.Core.Engine.Interfaces
{
@ -57,10 +58,16 @@ namespace Ombi.Core.Engine.Interfaces
}
}
public IEnumerable<RuleResult> RunRules(BaseRequestModel model)
public IEnumerable<RuleResult> RunRequestRules(BaseRequestModel model)
{
var ruleResults = Rules.StartRequestRules(model).ToList();
return ruleResults;
}
public IEnumerable<RuleResult> RunSearchRules(SearchViewModel model)
{
var ruleResults = Rules.StartSearchRules(model).ToList();
return ruleResults;
}
}
}

@ -97,7 +97,7 @@ namespace Ombi.Core.Engine
try
{
var ruleResults = RunRules(requestModel).ToList();
var ruleResults = RunRequestRules(requestModel).ToList();
if (ruleResults.Any(x => !x.Success))
return new RequestEngineResult
{

@ -135,6 +135,7 @@ namespace Ombi.Core.Engine
Dictionary<int, MovieRequestModel> existingRequests, PlexSettings plexSettings, EmbySettings embySettings)
{
var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id);
viewMovie.Id = showInfo.Id; // TheMovieDbId
if (plexSettings.Enable)
{
var item = await PlexContentRepo.Get(showInfo.ImdbId);
@ -178,6 +179,8 @@ namespace Ombi.Core.Engine
viewMovie.Available = requestedMovie.Available;
}
RunSearchRules(viewMovie);
return viewMovie;
}

@ -97,30 +97,45 @@ namespace Ombi.Core.Engine
{
var latest = showInfo.Season.OrderBy(x => x.SeasonNumber).FirstOrDefault();
foreach (var modelSeasonRequest in childRequest.SeasonRequests)
{
if (modelSeasonRequest.SeasonNumber == latest.SeasonNumber)
{
foreach (var episodesRequested in modelSeasonRequest.Episodes)
{
episodesRequested.Requested = true;
}
}
}
}
if (tv.FirstSeason)
{
var first = showInfo.Season.OrderByDescending(x => x.SeasonNumber).FirstOrDefault();
foreach (var modelSeasonRequest in childRequest.SeasonRequests)
{
if (modelSeasonRequest.SeasonNumber == first.SeasonNumber)
{
foreach (var episodesRequested in modelSeasonRequest.Episodes)
{
episodesRequested.Requested = true;
}
}
}
}
var ruleResults = RunRules(model).ToList();
var ruleResults = RunRequestRules(model).ToList();
if (ruleResults.Any(x => !x.Success))
{
return new RequestEngineResult
{
ErrorMessage = ruleResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message
};
}
var existingRequest = await TvRequestService.CheckRequestAsync(model.Id);
if (existingRequest != null)
{
return await AddExistingRequest(model, existingRequest);
}
// This is a new request
return await AddRequest(model);
}

@ -8,7 +8,6 @@ namespace Ombi.Core.Models.Search
public bool Adult { get; set; }
public string BackdropPath { get; set; }
public List<int> GenreIds { get; set; }
public int Id { get; set; }
public string OriginalLanguage { get; set; }
public string OriginalTitle { get; set; }
public string Overview { get; set; }

@ -2,6 +2,7 @@
{
public class SearchViewModel
{
public int Id { get; set; }
public bool Approved { get; set; }
public bool Requested { get; set; }
public bool Available { get; set; }

@ -22,6 +22,7 @@
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />

@ -0,0 +1,15 @@
namespace Ombi.Core.Rule
{
public abstract class BaseRequestRule
{
public RuleResult Success()
{
return new RuleResult {Success = true};
}
public RuleResult Fail(string message)
{
return new RuleResult {Message = message};
}
}
}

@ -1,6 +1,6 @@
namespace Ombi.Core.Rule
{
public abstract class BaseRule
public abstract class BaseSearchRule
{
public RuleResult Success()
{

@ -1,9 +1,8 @@
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule;
namespace Ombi.Core.Rules
namespace Ombi.Core.Rule.Interfaces
{
public interface IRequestRules<T> where T : BaseRequestModel
public interface IRequestRules<T> where T : new()
{
RuleResult Execute(T obj);
}

@ -1,11 +1,13 @@
using Ombi.Core.Models.Requests;
using Ombi.Core.Rule;
using System.Collections.Generic;
using Ombi.Core.Models.Search;
namespace Ombi.Core.Rules
{
public interface IRuleEvaluator
{
IEnumerable<RuleResult> StartRequestRules(BaseRequestModel obj);
IEnumerable<RuleResult> StartSearchRules(SearchViewModel obj);
}
}

@ -4,6 +4,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
namespace Ombi.Core.Rule
{
@ -12,12 +14,15 @@ namespace Ombi.Core.Rule
public RuleEvaluator(IServiceProvider provider)
{
RequestRules = new List<IRequestRules<BaseRequestModel>>();
var baseType = typeof(BaseRule).FullName;
SearchRules = new List<IRequestRules<SearchViewModel>>();
var baseSearchType = typeof(BaseSearchRule).FullName;
var baseRequestType = typeof(BaseRequestRule).FullName;
var ass = typeof(RuleEvaluator).GetTypeInfo().Assembly;
foreach (var ti in ass.DefinedTypes)
if (ti?.BaseType?.FullName == baseType)
{
if (ti?.BaseType?.FullName == baseSearchType)
{
var type = ti?.AsType();
var ctors = type.GetConstructors();
@ -25,14 +30,37 @@ namespace Ombi.Core.Rule
var services = new List<object>();
foreach (var param in ctor.GetParameters())
{
services.Add(provider.GetService(param.ParameterType));
}
var item = Activator.CreateInstance(type, services.ToArray()); // ti.GetType is wrong
var item = Activator.CreateInstance(type, services.ToArray());
RequestRules.Add((IRequestRules<BaseRequestModel>) item);
}
}
foreach (var ti in ass.DefinedTypes)
{
if (ti?.BaseType?.FullName == baseRequestType)
{
var type = ti?.AsType();
var ctors = type.GetConstructors();
var ctor = ctors.FirstOrDefault();
var services = new List<object>();
foreach (var param in ctor.GetParameters())
{
services.Add(provider.GetService(param.ParameterType));
}
var item = Activator.CreateInstance(type, services.ToArray());
SearchRules.Add((IRequestRules<SearchViewModel>) item);
}
}
}
private List<IRequestRules<BaseRequestModel>> RequestRules { get; }
private List<IRequestRules<SearchViewModel>> SearchRules { get; }
public IEnumerable<RuleResult> StartRequestRules(BaseRequestModel obj)
{
@ -45,5 +73,17 @@ namespace Ombi.Core.Rule
return results;
}
public IEnumerable<RuleResult> StartSearchRules(SearchViewModel obj)
{
var results = new List<RuleResult>();
foreach (var rule in SearchRules)
{
var result = rule.Execute(obj);
results.Add(result);
}
return results;
}
}
}

@ -3,10 +3,11 @@ using Ombi.Core.Models.Requests;
using Ombi.Core.Rules;
using Ombi.Store.Entities;
using System.Security.Principal;
using Ombi.Core.Rule.Interfaces;
namespace Ombi.Core.Rule.Rules
{
public class AutoApproveRule : BaseRule, IRequestRules<BaseRequestModel>
public class AutoApproveRule : BaseRequestRule, IRequestRules<BaseRequestModel>
{
public AutoApproveRule(IPrincipal principal)
{

@ -3,10 +3,11 @@ using Ombi.Core.Models.Requests;
using Ombi.Core.Rules;
using Ombi.Store.Entities;
using System.Security.Principal;
using Ombi.Core.Rule.Interfaces;
namespace Ombi.Core.Rule.Rules
{
public class CanRequestRule : BaseRule, IRequestRules<BaseRequestModel>
public class CanRequestRule : BaseRequestRule, IRequestRules<BaseRequestModel>
{
public CanRequestRule(IPrincipal principal)
{

@ -0,0 +1,31 @@
using System.Linq;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search;
using Ombi.Core.Rule.Interfaces;
using Ombi.Store.Context;
namespace Ombi.Core.Rule.Rules.Search
{
public class RadarrCacheRule : BaseSearchRule, IRequestRules<SearchViewModel>
{
public RadarrCacheRule(IOmbiContext ctx)
{
_ctx = ctx;
}
private readonly IOmbiContext _ctx;
public RuleResult Execute(SearchViewModel obj)
{
// Check if it's in Radarr
var result = _ctx.RadarrCache.FirstOrDefault(x => x.TheMovieDbId == obj.Id);
if (result != null)
{
obj.Approved = true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something?
}
return Success();
}
}
}

@ -24,6 +24,7 @@ using Ombi.Settings.Settings;
using Ombi.Store.Context;
using Ombi.Store.Repository;
using Ombi.Core.Rules;
using Ombi.Schedule.Jobs.Radarr;
namespace Ombi.DependencyInjection
{
@ -83,6 +84,7 @@ namespace Ombi.DependencyInjection
{
services.AddTransient<IPlexContentCacher, PlexContentCacher>();
services.AddTransient<IJobSetup, JobSetup>();
services.AddTransient<IRadarrCacher, RadarrCacher>();
}
public static void RegisterIdentity(this IServiceCollection services)

@ -1,20 +1,24 @@
using System;
using Hangfire;
using Hangfire;
using Ombi.Schedule.Jobs;
using Ombi.Schedule.Jobs.Radarr;
namespace Ombi.Schedule
{
public class JobSetup : IJobSetup
{
public JobSetup(IPlexContentCacher cacher)
public JobSetup(IPlexContentCacher cacher, IRadarrCacher radarrCacher)
{
Cacher = cacher;
RadarrCacher = radarrCacher;
}
private IPlexContentCacher Cacher { get; }
private IRadarrCacher RadarrCacher { get; }
public void Setup()
{
RecurringJob.AddOrUpdate(() => Cacher.CacheContent(), Cron.Hourly);
RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly);
}
}
}

@ -1,7 +1,9 @@
namespace Ombi.Schedule.Jobs
using System.Threading.Tasks;
namespace Ombi.Schedule.Jobs
{
public interface IPlexContentCacher
{
void CacheContent();
Task CacheContent();
}
}

@ -55,7 +55,7 @@ namespace Ombi.Schedule.Jobs
private ILogger<PlexContentCacher> Logger { get; }
private IPlexContentRepository Repo { get; }
public void CacheContent()
public async Task CacheContent()
{
var plexSettings = Plex.GetSettings();
if (!plexSettings.Enable)
@ -70,8 +70,7 @@ namespace Ombi.Schedule.Jobs
Logger.LogInformation("Starting Plex Content Cacher");
try
{
StartTheCache(plexSettings).Wait();
await StartTheCache(plexSettings);
}
catch (Exception e)
{

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Store.Entities;
namespace Ombi.Schedule.Jobs.Radarr
{
public interface IRadarrCacher
{
Task CacheContent();
Task<IEnumerable<RadarrCache>> GetCachedContent();
}
}

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Ombi.Api.Radarr;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Settings.Settings.Models.External;
using Ombi.Store.Context;
using Ombi.Store.Entities;
using Serilog;
namespace Ombi.Schedule.Jobs.Radarr
{
public class RadarrCacher : IRadarrCacher
{
public RadarrCacher(ISettingsService<RadarrSettings> radarr, IRadarrApi radarrApi, ILogger<RadarrCacher> log, IOmbiContext ctx)
{
RadarrSettings = radarr;
RadarrApi = radarrApi;
Logger = log;
_ctx = ctx;
}
private ISettingsService<RadarrSettings> RadarrSettings { get; }
private IRadarrApi RadarrApi { get; }
private ILogger<RadarrCacher> Logger { get; }
private readonly IOmbiContext _ctx;
public async Task CacheContent()
{
var settings = RadarrSettings.GetSettings();
if (settings.Enabled)
{
try
{
var movies = await RadarrApi.GetMovies(settings.ApiKey, settings.FullUri);
if (movies != null)
{
// Let's remove the old cached data
await _ctx.Database.ExecuteSqlCommandAsync("TRUNCATE TABLE RadarrCache");
var movieIds = new List<RadarrCache>();
foreach (var m in movies)
{
if (m.tmdbId > 0)
{
movieIds.Add(new RadarrCache{TheMovieDbId = m.tmdbId});
}
else
{
Log.Error("TMDBId is not > 0 for movie {0}", m.title);
}
}
await _ctx.RadarrCache.AddRangeAsync(movieIds);
await _ctx.SaveChangesAsync();
}
}
catch (System.Exception ex)
{
Logger.LogError(LoggingEvents.CacherException, ex, "Failed caching queued items from Radarr");
}
}
}
public async Task<IEnumerable<RadarrCache>> GetCachedContent()
{
return await _ctx.RadarrCache.ToListAsync();
}
}
}

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
</ItemGroup>

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Ombi.Store.Entities;
namespace Ombi.Store.Context
@ -14,6 +15,8 @@ namespace Ombi.Store.Context
DbSet<RequestBlobs> Requests { get; set; }
DbSet<GlobalSettings> Settings { get; set; }
DbSet<PlexContent> PlexContent { get; set; }
DbSet<RadarrCache> RadarrCache { get; set; }
DatabaseFacade Database { get; }
DbSet<User> Users { get; set; }
EntityEntry<T> Entry<T>(T entry) where T : class;
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;

@ -1,8 +1,5 @@
using System;
using System.IO;
using System.Resources;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Ombi.Store.Entities;
namespace Ombi.Store.Context
@ -35,12 +32,7 @@ namespace Ombi.Store.Context
public DbSet<GlobalSettings> Settings { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<PlexContent> PlexContent { get; set; }
public EntityEntry<T> Entry<T>(T entry) where T : class
{
return base.Entry(entry);
}
public DbSet<RadarrCache> RadarrCache { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities
{
[Table("RadarrCache")]
public class RadarrCache : Entity
{
public int TheMovieDbId { get; set; }
}
}

@ -17,6 +17,12 @@ CREATE TABLE IF NOT EXISTS PlexContent
ReleaseYear varchar(100) NOT NULL
);
CREATE TABLE IF NOT EXISTS RadarrCache
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
TheMovieDbId INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS SeasonsContent
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,

@ -12,14 +12,14 @@ namespace Ombi.Api.TheMovieDb
{
public TheMovieDbApi(IMapper mapper)
{
Api = new Ombi.Api.Api();
Api = new Api();
Mapper = mapper;
}
private IMapper Mapper { get; }
private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
private static readonly string BaseUri ="http://api.themoviedb.org/3/";
private Ombi.Api.Api Api { get; }
private Api Api { get; }
public async Task<MovieResponseDto> GetMovieInformation(int movieId)
{

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save