From 438251d8c71daf712082b387ccd31651e9160e7a Mon Sep 17 00:00:00 2001 From: TidusJar Date: Mon, 30 Jul 2018 09:57:27 +0100 Subject: [PATCH] Started on some user stats !wip --- src/Ombi.Core/Engine/MovieRequestEngine.cs | 1 + src/Ombi.Core/Engine/TvRequestEngine.cs | 1 + src/Ombi.Core/Engine/UserStatsEngine.cs | 98 +++++++++++++++++++ .../Jobs/Emby/EmbyAvaliabilityChecker.cs | 2 + .../Jobs/Plex/PlexAvailabilityChecker.cs | 2 + .../Entities/Requests/BaseRequest.cs | 3 + src/Ombi/package-lock.json | 48 ++++----- 7 files changed, 131 insertions(+), 24 deletions(-) create mode 100644 src/Ombi.Core/Engine/UserStatsEngine.cs diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 15383ed12..f73c6fda1 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -452,6 +452,7 @@ namespace Ombi.Core.Engine } request.Available = true; + request.MarkedAsAvailable = DateTime.Now; NotificationHelper.Notify(request, NotificationType.RequestAvailable); await MovieRepository.Update(request); diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index e8fc65a26..e952ac010 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -516,6 +516,7 @@ namespace Ombi.Core.Engine }; } request.Available = true; + request.MarkedAsAvailable = DateTime.Now; foreach (var season in request.SeasonRequests) { foreach (var e in season.Episodes) diff --git a/src/Ombi.Core/Engine/UserStatsEngine.cs b/src/Ombi.Core/Engine/UserStatsEngine.cs new file mode 100644 index 000000000..f416d56e3 --- /dev/null +++ b/src/Ombi.Core/Engine/UserStatsEngine.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Store.Entities; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Core.Engine +{ + public class UserStatsEngine + { + public UserStatsEngine(OmbiUserManager um, IMovieRequestRepository movieRequest, ITvRequestRepository tvRequest) + { + _userManager = um; + _movieRequest = movieRequest; + _tvRequest = tvRequest; + } + + private readonly OmbiUserManager _userManager; + private readonly IMovieRequestRepository _movieRequest; + private readonly ITvRequestRepository _tvRequest; + + public async Task GetSummary(SummaryRequest request) + { + /* What do we want? + + This is Per week/month/all time (filter by date) + + 1. Total Requests + 2. Total Movie Requests + 3. Total Tv Requests + 4. Total Issues (If enabled) + 5. Total Requests fufilled (now available) + + Then + + 2. Most requested user Movie + 3. Most requested user tv + + Then + + 1. + + */ + + // get all movie requests + var movies = _movieRequest.GetWithUser(); + var filteredMovies = movies.Where(x => x.RequestedDate >= request.From && x.RequestedDate <= request.To); + var tv = _tvRequest.GetLite(); + var children = tv.SelectMany(x => + x.ChildRequests.Where(c => c.RequestedDate >= request.From && c.RequestedDate <= request.To)); + + var moviesCount = filteredMovies.CountAsync(); + var childrenCount = children.CountAsync(); + var availableMovies = + movies.Select(x => x.MarkedAsAvailable >= request.From && x.MarkedAsAvailable <= request.To).CountAsync(); + var availableChildren = tv.SelectMany(x => + x.ChildRequests.Where(c => c.MarkedAsAvailable >= request.From && c.MarkedAsAvailable <= request.To)).CountAsync(); + + var userMovie = filteredMovies.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync(); + var userTv = children.GroupBy(x => x.RequestedUserId).OrderBy(x => x.Key).FirstOrDefaultAsync(); + + + return new UserStatsSummary + { + TotalMovieRequests = await moviesCount, + TotalTvRequests = await childrenCount, + CompletedRequestsTv = await availableChildren, + CompletedRequestsMovies = await availableMovies, + MostRequestedUserMovie = (await userMovie).FirstOrDefault().RequestedUser, + MostRequestedUserTv = (await userTv).FirstOrDefault().RequestedUser, + }; + } + } + + public class SummaryRequest + { + public DateTime From { get; set; } + public DateTime To { get; set; } + } + + public class UserStatsSummary + { + public int TotalRequests => TotalTvRequests + TotalTvRequests; + public int TotalMovieRequests { get; set; } + public int TotalTvRequests { get; set; } + public int TotalIssues { get; set; } + public int CompletedRequestsMovies { get; set; } + public int CompletedRequestsTv { get; set; } + public int CompletedRequests => CompletedRequestsMovies + CompletedRequestsTv; + public OmbiUser MostRequestedUserMovie { get; set; } + public OmbiUser MostRequestedUserTv { get; set; } + + } +} diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index ec675ebd8..7007b3743 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -89,6 +89,7 @@ namespace Ombi.Schedule.Jobs.Emby _log.LogInformation("We have found the request {0} on Emby, sending the notification", movie?.Title ?? string.Empty); movie.Available = true; + movie.MarkedAsAvailable = DateTime.Now; if (movie.Available) { var recipient = movie.RequestedUser.Email.HasValue() ? movie.RequestedUser.Email : string.Empty; @@ -185,6 +186,7 @@ namespace Ombi.Schedule.Jobs.Emby { // We have fulfulled this request! child.Available = true; + child.MarkedAsAvailable = DateTime.Now; BackgroundJob.Enqueue(() => _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index 9978b7e2b..e0278f854 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -123,6 +123,7 @@ namespace Ombi.Schedule.Jobs.Plex { // We have fulfulled this request! child.Available = true; + child.MarkedAsAvailable = DateTime.Now; _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, @@ -163,6 +164,7 @@ namespace Ombi.Schedule.Jobs.Plex } movie.Available = true; + movie.MarkedAsAvailable = DateTime.Now; if (movie.Available) { _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions diff --git a/src/Ombi.Store/Entities/Requests/BaseRequest.cs b/src/Ombi.Store/Entities/Requests/BaseRequest.cs index 95395d0bf..f224032f1 100644 --- a/src/Ombi.Store/Entities/Requests/BaseRequest.cs +++ b/src/Ombi.Store/Entities/Requests/BaseRequest.cs @@ -8,10 +8,13 @@ namespace Ombi.Store.Entities.Requests { public string Title { get; set; } public bool Approved { get; set; } + public DateTime MarkedAsApproved { get; set; } public DateTime RequestedDate { get; set; } public bool Available { get; set; } + public DateTime? MarkedAsAvailable { get; set; } public string RequestedUserId { get; set; } public bool? Denied { get; set; } + public DateTime MarkedAsDenied { get; set; } public string DeniedReason { get; set; } public RequestType RequestType { get; set; } diff --git a/src/Ombi/package-lock.json b/src/Ombi/package-lock.json index f5e18da7b..b7bd16f25 100644 --- a/src/Ombi/package-lock.json +++ b/src/Ombi/package-lock.json @@ -203,7 +203,7 @@ "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.4", @@ -447,7 +447,7 @@ "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=" + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "archy": { "version": "1.0.0", @@ -704,7 +704,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "requires": { "cache-base": "1.0.1", "class-utils": "0.3.5", @@ -932,7 +932,7 @@ "browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "requires": { "pako": "1.0.6" } @@ -1010,7 +1010,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "requires": { "collection-visit": "1.0.0", "component-emitter": "1.2.1", @@ -1572,7 +1572,7 @@ "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "requires": { "browserify-cipher": "1.0.0", "browserify-sign": "4.0.4", @@ -1785,7 +1785,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -2375,7 +2375,7 @@ "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "requires": { "md5.js": "1.3.4", "safe-buffer": "5.1.1" @@ -5612,7 +5612,7 @@ "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "requires": { "bn.js": "4.11.8", "brorand": "1.1.0" @@ -5844,7 +5844,7 @@ "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "requires": { "lower-case": "1.1.4" } @@ -5879,7 +5879,7 @@ "node-libs-browser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha1-X5QmPUBPbkR2fXJpAf/wVHjWAN8=", + "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", "requires": { "assert": "1.4.1", "browserify-zlib": "0.2.0", @@ -5963,7 +5963,7 @@ "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "requires": { "hosted-git-info": "2.5.0", "is-builtin-module": "1.0.0", @@ -9474,7 +9474,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "1.1.4", "console-control-strings": "1.1.0", @@ -9744,7 +9744,7 @@ "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha1-AQEhG6pwxLykoPY/Igbpe3368lg=" + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, "parallel-transform": { "version": "1.1.0", @@ -10023,7 +10023,7 @@ "postcss": { "version": "5.2.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "requires": { "chalk": "1.1.3", "js-base64": "2.3.2", @@ -10547,7 +10547,7 @@ "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=" + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" }, "process": { "version": "0.11.10", @@ -11102,7 +11102,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "schema-utils": { "version": "0.3.0", @@ -11198,7 +11198,7 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -11214,7 +11214,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "sha.js": { "version": "2.4.10", @@ -11353,7 +11353,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "requires": { "define-property": "1.0.0", "isobject": "3.0.1", @@ -11363,7 +11363,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "requires": { "kind-of": "3.2.2" }, @@ -11539,7 +11539,7 @@ "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha1-qqR0A/eyRakvvJfqCPJQ1gh+0IU=" + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==" }, "source-map": { "version": "0.5.7", @@ -11597,7 +11597,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "requires": { "extend-shallow": "3.0.2" }, @@ -11756,7 +11756,7 @@ "stream-each": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha1-joxGP5HaiZF3h2WHP+TZYNj2Fr0=", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "requires": { "end-of-stream": "1.4.1", "stream-shift": "1.0.0"