diff options
| author | Tavares André <tavares_and@hotmail.com> | 2015-06-11 20:25:12 +0200 |
|---|---|---|
| committer | Tavares André <tavares_and@hotmail.com> | 2015-06-11 20:25:12 +0200 |
| commit | 1e07dbec63bced51857e67b00941b91ca86a7f77 (patch) | |
| tree | 874b7e77f7f479bd5abef9ee6ee8314002398908 | |
| parent | ff26b095836a10ef2574417115e51afc6bf45768 (diff) | |
Reports - Add Users activities
19 files changed, 2783 insertions, 2087 deletions
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 0dfd812c3..3beb33b04 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -84,6 +84,8 @@ <Compile Include="Playback\MediaInfoService.cs" /> <Compile Include="Playback\TranscodingThrottler.cs" /> <Compile Include="PlaylistService.cs" /> + <Compile Include="Reports\Activities\ReportActivitiesBuilder.cs" /> + <Compile Include="Reports\Common\HeaderActivitiesMetadata.cs" /> <Compile Include="Reports\Common\HeaderMetadata.cs" /> <Compile Include="Reports\Common\ItemViewType.cs" /> <Compile Include="Reports\Common\ReportBuilderBase.cs" /> @@ -91,15 +93,16 @@ <Compile Include="Reports\Common\ReportFieldType.cs" /> <Compile Include="Reports\Common\ReportHeaderIdType.cs" /> <Compile Include="Reports\Common\ReportHelper.cs" /> + <Compile Include="Reports\Common\ReportIncludeItemTypes.cs" /> <Compile Include="Reports\Common\ReportViewType.cs" /> <Compile Include="Reports\Data\ReportBuilder.cs" /> <Compile Include="Reports\Data\ReportExport.cs" /> - <Compile Include="Reports\Data\ReportGroup.cs" /> - <Compile Include="Reports\Data\ReportHeader.cs" /> - <Compile Include="Reports\Data\ReportItem.cs" /> <Compile Include="Reports\Data\ReportOptions.cs" /> - <Compile Include="Reports\Data\ReportResult.cs" /> - <Compile Include="Reports\Data\ReportRow.cs" /> + <Compile Include="Reports\Model\ReportGroup.cs" /> + <Compile Include="Reports\Model\ReportHeader.cs" /> + <Compile Include="Reports\Model\ReportItem.cs" /> + <Compile Include="Reports\Model\ReportResult.cs" /> + <Compile Include="Reports\Model\ReportRow.cs" /> <Compile Include="Reports\ReportRequests.cs" /> <Compile Include="Reports\ReportsService.cs" /> <Compile Include="Reports\Stat\ReportStatBuilder.cs" /> @@ -204,6 +207,13 @@ <PostBuildEvent> </PostBuildEvent> </PropertyGroup> + <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" /> + </Target> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.Api/Reports/Activities/ReportActivitiesBuilder.cs b/MediaBrowser.Api/Reports/Activities/ReportActivitiesBuilder.cs new file mode 100644 index 000000000..4e2e85846 --- /dev/null +++ b/MediaBrowser.Api/Reports/Activities/ReportActivitiesBuilder.cs @@ -0,0 +1,240 @@ +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Querying; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +namespace MediaBrowser.Api.Reports +{ + /// <summary> A report activities builder. </summary> + /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/> + public class ReportActivitiesBuilder : ReportBuilderBase + { + #region [Constructors] + + /// <summary> + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportActivitiesBuilder class. </summary> + /// <param name="libraryManager"> Manager for library. </param> + /// <param name="userManager"> Manager for user. </param> + public ReportActivitiesBuilder(ILibraryManager libraryManager, IUserManager userManager) + : base(libraryManager) + { + _userManager = userManager; + } + + #endregion + + #region [Private Fields] + + private readonly IUserManager _userManager; ///< Manager for user + + #endregion + + #region [Public Methods] + + /// <summary> Gets a result. </summary> + /// <param name="queryResult"> The query result. </param> + /// <param name="request"> The request. </param> + /// <returns> The result. </returns> + public ReportResult GetResult(QueryResult<ActivityLogEntry> queryResult, IReportsQuery request) + { + List<ReportOptions<ActivityLogEntry>> options = this.GetReportOptions<ActivityLogEntry>(request, + () => this.GetDefaultHeaderMetadata(), + (hm) => this.GetOption(hm)).Where(x => x.Header.Visible == true).ToList(); + + var headers = GetHeaders<ActivityLogEntry>(options); + var rows = GetReportRows(queryResult.Items, options); + + ReportResult result = new ReportResult { Headers = headers }; + HeaderMetadata groupBy = ReportHelper.GetHeaderMetadataType(request.GroupBy); + int i = headers.FindIndex(x => x.FieldName == groupBy); + if (groupBy != HeaderMetadata.None && i >= 0) + { + var rowsGroup = rows.SelectMany(x => x.Columns[i].Name.Split(';'), (x, g) => new { Group = g.Trim(), Rows = x }) + .GroupBy(x => x.Group) + .OrderBy(x => x.Key) + .Select(x => new ReportGroup { Name = x.Key, Rows = x.Select(r => r.Rows).ToList() }); + + result.Groups = rowsGroup.ToList(); + result.IsGrouped = true; + } + else + { + result.Rows = rows; + result.IsGrouped = false; + } + + return result; + } + + #endregion + + #region [Protected Internal Methods] + + /// <summary> Gets the headers. </summary> + /// <typeparam name="H"> Type of the header. </typeparam> + /// <param name="request"> The request. </param> + /// <returns> The headers. </returns> + /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/> + protected internal override List<ReportHeader> GetHeaders<H>(H request) + { + return this.GetHeaders<ActivityLogEntry>(request, () => this.GetDefaultHeaderMetadata(), (hm) => this.GetOption(hm)); + } + + #endregion + + #region [Private Methods] + + /// <summary> Gets default header metadata. </summary> + /// <returns> The default header metadata. </returns> + private List<HeaderMetadata> GetDefaultHeaderMetadata() + { + return new List<HeaderMetadata> + { + HeaderMetadata.Date, + HeaderMetadata.User, + HeaderMetadata.Type, + HeaderMetadata.Severity, + HeaderMetadata.Name, + HeaderMetadata.ShortOverview, + HeaderMetadata.Overview, + //HeaderMetadata.UserPrimaryImageTag, + //HeaderMetadata.Item, + }; + } + + /// <summary> Gets an option. </summary> + /// <param name="header"> The header. </param> + /// <param name="sortField"> The sort field. </param> + /// <returns> The option. </returns> + private ReportOptions<ActivityLogEntry> GetOption(HeaderMetadata header, string sortField = "") + { + HeaderMetadata internalHeader = header; + + ReportOptions<ActivityLogEntry> option = new ReportOptions<ActivityLogEntry>() + { + Header = new ReportHeader + { + HeaderFieldType = ReportFieldType.String, + SortField = sortField, + Type = "", + ItemViewType = ItemViewType.None + } + }; + + switch (header) + { + case HeaderMetadata.StatusImage: + option.Header.ItemViewType = ItemViewType.StatusImage; + internalHeader = HeaderMetadata.Status; + option.Header.CanGroup = false; + break; + case HeaderMetadata.Name: + option.Column = (i, r) => i.Name; + break; + case HeaderMetadata.Overview: + option.Column = (i, r) => i.Overview; + option.Header.SortField = ""; + option.Header.CanGroup = false; + break; + + case HeaderMetadata.ShortOverview: + option.Column = (i, r) => i.ShortOverview; + option.Header.SortField = ""; + option.Header.CanGroup = false; + break; + + case HeaderMetadata.Type: + option.Column = (i, r) => i.Type; + option.Header.SortField = ""; + break; + + case HeaderMetadata.Date: + option.Column = (i, r) => i.Date; + option.Header.SortField = ""; + option.Header.HeaderFieldType = ReportFieldType.DateTime; + option.Header.Type = ""; + break; + + case HeaderMetadata.UserPrimaryImageTag: + option.Column = (i, r) => i.UserPrimaryImageTag; + option.Header.SortField = ""; + break; + case HeaderMetadata.Severity: + option.Column = (i, r) => i.Severity; + option.Header.SortField = ""; + break; + case HeaderMetadata.Item: + option.Column = (i, r) => i.ItemId; + option.Header.SortField = ""; + break; + case HeaderMetadata.User: + option.Column = (i, r) => + { + if (!string.IsNullOrEmpty(i.UserId)) + { + MediaBrowser.Controller.Entities.User user = _userManager.GetUserById(i.UserId); + if (user != null) + return user.Name; + } + return string.Empty; + }; + option.Header.SortField = ""; + break; + + } + + option.Header.Name = GetLocalizedHeader(internalHeader); + option.Header.FieldName = header; + + return option; + } + + /// <summary> Gets report rows. </summary> + /// <param name="items"> The items. </param> + /// <param name="options"> Options for controlling the operation. </param> + /// <returns> The report rows. </returns> + private List<ReportRow> GetReportRows(IEnumerable<ActivityLogEntry> items, List<ReportOptions<ActivityLogEntry>> options) + { + var rows = new List<ReportRow>(); + + foreach (ActivityLogEntry item in items) + { + ReportRow rRow = GetRow(item); + foreach (ReportOptions<ActivityLogEntry> option in options) + { + object itemColumn = option.Column != null ? option.Column(item, rRow) : ""; + object itemId = option.ItemID != null ? option.ItemID(item) : ""; + ReportItem rItem = new ReportItem + { + Name = ReportHelper.ConvertToString(itemColumn, option.Header.HeaderFieldType), + Id = ReportHelper.ConvertToString(itemId, ReportFieldType.Object) + }; + rRow.Columns.Add(rItem); + } + + rows.Add(rRow); + } + + return rows; + } + + /// <summary> Gets a row. </summary> + /// <param name="item"> The item. </param> + /// <returns> The row. </returns> + private ReportRow GetRow(ActivityLogEntry item) + { + ReportRow rRow = new ReportRow + { + Id = item.Id, + + }; + return rRow; + } + + #endregion + + } +} diff --git a/MediaBrowser.Api/Reports/Common/HeaderActivitiesMetadata.cs b/MediaBrowser.Api/Reports/Common/HeaderActivitiesMetadata.cs new file mode 100644 index 000000000..69c0ff234 --- /dev/null +++ b/MediaBrowser.Api/Reports/Common/HeaderActivitiesMetadata.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Api.Reports +{ + public enum HeaderActivitiesMetadata + { + None, + Name, + Overview, + ShortOverview, + Type, + Date, + UserPrimaryImageTag, + Severity, + Item, + User + } +} diff --git a/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs b/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs index 3cb8f722d..f61331aa7 100644 --- a/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs +++ b/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs @@ -42,6 +42,15 @@ namespace MediaBrowser.Api.Reports AudioAlbumArtist, MusicArtist, AudioAlbum, - Status + Status, + //Activity logs + Overview, + ShortOverview, + Type, + Date, + UserPrimaryImageTag, + Severity, + Item, + User } } diff --git a/MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs b/MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs index af6dc997c..939bf280a 100644 --- a/MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs +++ b/MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs @@ -13,217 +13,330 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { - /// <summary> A report builder base. </summary> - public class ReportBuilderBase - { - /// <summary> - /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilderBase class. </summary> - /// <param name="libraryManager"> Manager for library. </param> - public ReportBuilderBase(ILibraryManager libraryManager) - { - _libraryManager = libraryManager; - } - - /// <summary> Manager for library. </summary> - protected readonly ILibraryManager _libraryManager; - - /// <summary> Gets audio stream. </summary> - /// <param name="item"> The item. </param> - /// <returns> The audio stream. </returns> - protected string GetAudioStream(BaseItem item) - { - var stream = GetStream(item, MediaStreamType.Audio); - if (stream != null) - return stream.Codec.ToUpper() == "DCA" ? stream.Profile : stream.Codec. - ToUpper(); - - return string.Empty; - } - - /// <summary> Gets an episode. </summary> - /// <param name="item"> The item. </param> - /// <returns> The episode. </returns> - protected string GetEpisode(BaseItem item) - { - - if (item.GetClientTypeName() == ChannelMediaContentType.Episode.ToString() && item.ParentIndexNumber != null) - return "Season " + item.ParentIndexNumber; - else - return item.Name; - } - - /// <summary> Gets a genre. </summary> - /// <param name="name"> The name. </param> - /// <returns> The genre. </returns> - protected Genre GetGenre(string name) - { - if (string.IsNullOrEmpty(name)) - return null; - return _libraryManager.GetGenre(name); - } - - /// <summary> Gets genre identifier. </summary> - /// <param name="name"> The name. </param> - /// <returns> The genre identifier. </returns> - protected string GetGenreID(string name) - { - if (string.IsNullOrEmpty(name)) - return string.Empty; - return string.Format("{0:N}", - GetGenre(name).Id); - } - - /// <summary> Gets list as string. </summary> - /// <param name="items"> The items. </param> - /// <returns> The list as string. </returns> - protected string GetListAsString(List<string> items) - { - return String.Join("; ", items); - } - - /// <summary> Gets media source information. </summary> - /// <param name="item"> The item. </param> - /// <returns> The media source information. </returns> - protected MediaSourceInfo GetMediaSourceInfo(BaseItem item) - { - var mediaSource = item as IHasMediaSources; - if (mediaSource != null) - return mediaSource.GetMediaSources(false).FirstOrDefault(n => n.Type == MediaSourceType.Default); - - return null; - } - - /// <summary> Gets an object. </summary> - /// <typeparam name="T"> Generic type parameter. </typeparam> - /// <typeparam name="R"> Type of the r. </typeparam> - /// <param name="item"> The item. </param> - /// <param name="function"> The function. </param> - /// <param name="defaultValue"> The default value. </param> - /// <returns> The object. </returns> - protected R GetObject<T, R>(BaseItem item, Func<T, R> function, R defaultValue = default(R)) where T : class - { - var value = item as T; - if (value != null && function != null) - return function(value); - else - return defaultValue; - } - - /// <summary> Gets a person. </summary> - /// <param name="name"> The name. </param> - /// <returns> The person. </returns> - protected Person GetPerson(string name) - { - if (string.IsNullOrEmpty(name)) - return null; - return _libraryManager.GetPerson(name); - } - - /// <summary> Gets person identifier. </summary> - /// <param name="name"> The name. </param> - /// <returns> The person identifier. </returns> - protected string GetPersonID(string name) - { - if (string.IsNullOrEmpty(name)) - return string.Empty; - return string.Format("{0:N}", - GetPerson(name).Id); - } - - /// <summary> Gets runtime date time. </summary> - /// <param name="runtime"> The runtime. </param> - /// <returns> The runtime date time. </returns> - protected double? GetRuntimeDateTime(long? runtime) - { - if (runtime.HasValue) + /// <summary> A report builder base. </summary> + public abstract class ReportBuilderBase + { + + #region [Constructors] + + /// <summary> + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilderBase class. </summary> + /// <param name="libraryManager"> Manager for library. </param> + public ReportBuilderBase(ILibraryManager libraryManager) + { + _libraryManager = libraryManager; + } + + #endregion + + #region [Protected Fields] + + /// <summary> Manager for library. </summary> + protected readonly ILibraryManager _libraryManager; ///< Manager for library + + protected Func<bool, string> GetBoolString = s => s == true ? "x" : ""; ///< . + + #endregion + + #region [Protected Internal Methods] + + /// <summary> Gets the headers. </summary> + /// <typeparam name="H"> Type of the header. </typeparam> + /// <param name="request"> The request. </param> + /// <returns> The headers. </returns> + protected internal abstract List<ReportHeader> GetHeaders<H>(H request) where H : IReportsHeader; + + #endregion + + #region [Protected Methods] + + /// <summary> Gets active headers. </summary> + /// <typeparam name="T"> Generic type parameter. </typeparam> + /// <param name="options"> Options for controlling the operation. </param> + /// <returns> The active headers. </returns> + protected List<ReportHeader> GetActiveHeaders<T>(List<ReportOptions<T>> options) + { + List<ReportHeader> headers = new List<ReportHeader>(); + foreach (ReportOptions<T> option in options.Where(x => x.Header.Visible == true)) + { + headers.Add(option.Header); + } + + return headers; + } + + /// <summary> Gets audio stream. </summary> + /// <param name="item"> The item. </param> + /// <returns> The audio stream. </returns> + protected string GetAudioStream(BaseItem item) + { + var stream = GetStream(item, MediaStreamType.Audio); + if (stream != null) + return stream.Codec.ToUpper() == "DCA" ? stream.Profile : stream.Codec. + ToUpper(); + + return string.Empty; + } + + /// <summary> Gets an episode. </summary> + /// <param name="item"> The item. </param> + /// <returns> The episode. </returns> + protected string GetEpisode(BaseItem item) + { + + if (item.GetClientTypeName() == ChannelMediaContentType.Episode.ToString() && item.ParentIndexNumber != null) + return "Season " + item.ParentIndexNumber; + else + return item.Name; + } + + /// <summary> Gets a genre. </summary> + /// <param name="name"> The name. </param> + /// <returns> The genre. </returns> + protected Genre GetGenre(string name) + { + if (string.IsNullOrEmpty(name)) + return null; + return _libraryManager.GetGenre(name); + } + + /// <summary> Gets genre identifier. </summary> + /// <param name="name"> The name. </param> + /// <returns> The genre identifier. </returns> + protected string GetGenreID(string name) + { + if (string.IsNullOrEmpty(name)) + return string.Empty; + return string.Format("{0:N}", + GetGenre(name).Id); + } + + /// <summary> Gets the headers. </summary> + /// <typeparam name="T"> Generic type parameter. </typeparam> + /// <param name="options"> Options for controlling the operation. </param> + /// <returns> The headers. </returns> + protected List<ReportHeader> GetHeaders<T>(List<ReportOptions<T>> options) + { + List<ReportHeader> headers = new List<ReportHeader>(); + foreach (ReportOptions<T> option in options) + { + headers.Add(option.Header); + } + + return headers; + } + + /// <summary> Gets the headers. </summary> + /// <typeparam name="T"> Generic type parameter. </typeparam> + /// <param name="request"> The request. </param> + /// <param name="getHeadersMetadata"> The get headers metadata. </param> + /// <param name="getOptions"> Options for controlling the get. </param> + /// <returns> The headers. </returns> + protected List<ReportHeader> GetHeaders<T>(IReportsHeader request, Func<List<HeaderMetadata>> getHeadersMetadata, Func<HeaderMetadata, ReportOptions<T>> getOptions) + { + List<ReportOptions<T>> options = this.GetReportOptions(request, getHeadersMetadata, getOptions); + return this.GetHeaders(options); + } + + /// <summary> Gets list as string. </summary> + /// <param name="items"> The items. </param> + /// <returns> The list as string. </returns> + protected string GetListAsString(List<string> items) + { + return String.Join("; ", items); + } + + /// <summary> Gets localized header. </summary> + /// <param name="internalHeader"> The internal header. </param> + /// <returns> The localized header. </returns> + protected static string GetLocalizedHeader(HeaderMetadata internalHeader) + { + string headerName = ""; + if (internalHeader != HeaderMetadata.None) + { + string localHeader = "Header" + internalHeader.ToString(); + headerName = internalHeader != HeaderMetadata.None ? ReportHelper.GetJavaScriptLocalizedString(localHeader) : ""; + if (string.Compare(localHeader, headerName, StringComparison.CurrentCultureIgnoreCase) == 0) + headerName = ReportHelper.GetServerLocalizedString(localHeader); + } + return headerName; + } + + /// <summary> Gets media source information. </summary> + /// <param name="item"> The item. </param> + /// <returns> The media source information. </returns> + protected MediaSourceInfo GetMediaSourceInfo(BaseItem item) + { + var mediaSource = item as IHasMediaSources; + if (mediaSource != null) + return mediaSource.GetMediaSources(false).FirstOrDefault(n => n.Type == MediaSourceType.Default); + + return null; + } + + /// <summary> Gets an object. </summary> + /// <typeparam name="T"> Generic type parameter. </typeparam> + /// <typeparam name="R"> Type of the r. </typeparam> + /// <param name="item"> The item. </param> + /// <param name="function"> The function. </param> + /// <param name="defaultValue"> The default value. </param> + /// <returns> The object. </returns> + protected R GetObject<T, R>(BaseItem item, Func<T, R> function, R defaultValue = default(R)) where T : class + { + var value = item as T; + if (value != null && function != null) + return function(value); + else + return defaultValue; + } + + /// <summary> Gets a person. </summary> + /// <param name="name"> The name. </param> + /// <returns> The person. </returns> + protected Person GetPerson(string name) + { + if (string.IsNullOrEmpty(name)) + return null; + return _libraryManager.GetPerson(name); + } + + /// <summary> Gets person identifier. </summary> + /// <param name="name"> The name. </param> + /// <returns> The person identifier. </returns> + protected string GetPersonID(string name) + { + if (string.IsNullOrEmpty(name)) + return string.Empty; + return string.Format("{0:N}", + GetPerson(name).Id); + } + + /// <summary> Gets report options. </summary> + /// <typeparam name="T"> Generic type parameter. </typeparam> + /// <param name="request"> The request. </param> + /// <param name="getHeadersMetadata"> The get headers metadata. </param> + /// <param name="getOptions"> Options for controlling the get. </param> + /// <returns> The report options. </returns> + protected List<ReportOptions<T>> GetReportOptions<T>(IReportsHeader request, Func<List<HeaderMetadata>> getHeadersMetadata, Func<HeaderMetadata, ReportOptions<T>> getOptions) + { + List<HeaderMetadata> headersMetadata = getHeadersMetadata(); + List<ReportOptions<T>> options = new List<ReportOptions<T>>(); + foreach (HeaderMetadata header in headersMetadata) + { + options.Add(getOptions(header)); + } + + if (request != null && !string.IsNullOrEmpty(request.ReportColumns)) + { + List<HeaderMetadata> headersMetadataFiltered = ReportHelper.GetFilteredReportHeaderMetadata(request.ReportColumns, () => headersMetadata); + foreach (ReportHeader header in options.Select(x => x.Header)) + { + if (!headersMetadataFiltered.Contains(header.FieldName)) + { + header.Visible = false; + } + } + } + + return options; + } + + /// <summary> Gets runtime date time. </summary> + /// <param name="runtime"> The runtime. </param> + /// <returns> The runtime date time. </returns> + protected double? GetRuntimeDateTime(long? runtime) + { + if (runtime.HasValue) return Math.Ceiling(new TimeSpan(runtime.Value).TotalMinutes); - return null; - } - - /// <summary> Gets series production year. </summary> - /// <param name="item"> The item. </param> - /// <returns> The series production year. </returns> - protected string GetSeriesProductionYear(BaseItem item) - { - - string productionYear = item.ProductionYear.ToString(); - var series = item as Series; - if (series == null) - { - if (item.ProductionYear == null || item.ProductionYear == 0) - return string.Empty; - return productionYear; - } - - if (series.Status == SeriesStatus.Continuing) - return productionYear += "-Present"; - - if (series.EndDate != null && series.EndDate.Value.Year != series.ProductionYear) - return productionYear += "-" + series.EndDate.Value.Year; - - return productionYear; - } - - /// <summary> Gets a stream. </summary> - /// <param name="item"> The item. </param> - /// <param name="streamType"> Type of the stream. </param> - /// <returns> The stream. </returns> - protected MediaStream GetStream(BaseItem item, MediaStreamType streamType) - { - var itemInfo = GetMediaSourceInfo(item); - if (itemInfo != null) - return itemInfo.MediaStreams.FirstOrDefault(n => n.Type == streamType); - - return null; - } - - /// <summary> Gets a studio. </summary> - /// <param name="name"> The name. </param> - /// <returns> The studio. </returns> - protected Studio GetStudio(string name) - { - if (string.IsNullOrEmpty(name)) - return null; - return _libraryManager.GetStudio(name); - } - - /// <summary> Gets studio identifier. </summary> - /// <param name="name"> The name. </param> - /// <returns> The studio identifier. </returns> - protected string GetStudioID(string name) - { - if (string.IsNullOrEmpty(name)) - return string.Empty; - return string.Format("{0:N}", - GetStudio(name).Id); - } - - /// <summary> Gets video resolution. </summary> - /// <param name="item"> The item. </param> - /// <returns> The video resolution. </returns> - protected string GetVideoResolution(BaseItem item) - { - var stream = GetStream(item, - MediaStreamType.Video); - if (stream != null && stream.Width != null) - return string.Format("{0} * {1}", - stream.Width, - (stream.Height != null ? stream.Height.ToString() : "-")); - - return string.Empty; - } - - /// <summary> Gets video stream. </summary> - /// <param name="item"> The item. </param> - /// <returns> The video stream. </returns> - protected string GetVideoStream(BaseItem item) - { - var stream = GetStream(item, MediaStreamType.Video); - if (stream != null) - return stream.Codec.ToUpper(); - - return string.Empty; - } - - } + return null; + } + + /// <summary> Gets series production year. </summary> + /// <param name="item"> The item. </param> + /// <returns> The series production year. </returns> + protected string GetSeriesProductionYear(BaseItem item) + { + + string productionYear = item.ProductionYear.ToString(); + var series = item as Series; + if (series == null) + { + if (item.ProductionYear == null || item.ProductionYear == 0) + return string.Empty; + return productionYear; + } + + if (series.Status == SeriesStatus.Continuing) + return productionYear += "-Present"; + + if (series.EndDate != null && series.EndDate.Value.Year != series.ProductionYear) + return productionYear += "-" + series.EndDate.Value.Year; + + return productionYear; + } + + /// <summary> Gets a stream. </summary> + /// <param name="item"> The item. </param> + /// <param name="streamType"> Type of the stream. </param> + /// <returns> The stream. </returns> + protected MediaStream GetStream(BaseItem item, MediaStreamType streamType) + { + var itemInfo = GetMediaSourceInfo(item); + if (itemInfo != null) + return itemInfo.MediaStreams.FirstOrDefault(n => n.Type == streamType); + + return null; + } + + /// <summary> Gets a studio. </summary> + /// <param name="name"> The name. </param> + /// <returns> The studio. </returns> + protected Studio GetStudio(string name) + { + if (string.IsNullOrEmpty(name)) + return null; + return _libraryManager.GetStudio(name); + } + + /// <summary> Gets studio identifier. </summary> + /// <param name="name"> The name. </param> + /// <returns> The studio identifier. </returns> + protected string GetStudioID(string name) + { + if (string.IsNullOrEmpty(name)) + return string.Empty; + return string.Format("{0:N}", + GetStudio(name).Id); + } + + /// <summary> Gets video resolution. </summary> + /// <param name="item"> The item. </param> + /// <returns> The video resolution. </returns> + protected string GetVideoResolution(BaseItem item) + { + var stream = GetStream(item, + MediaStreamType.Video); + if (stream != null && stream.Width != null) + return string.Format("{0} * {1}", + stream.Width, + (stream.Height != null ? stream.Height.ToString() : "-")); + + return string.Empty; + } + + /// <summary> Gets video stream. </summary> + /// <param name="item"> The item. </param> + /// <returns> The video stream. </returns> + protected string GetVideoStream(BaseItem item) + { + var stream = GetStream(item, MediaStreamType.Video); + if (stream != null) + return stream.Codec.ToUpper(); + + return string.Empty; + } + + #endregion + + } } diff --git a/MediaBrowser.Api/Reports/Common/ReportHelper.cs b/MediaBrowser.Api/Reports/Common/ReportHelper.cs index a557248c6..1de0190cf 100644 --- a/MediaBrowser.Api/Reports/Common/ReportHelper.cs +++ b/MediaBrowser.Api/Reports/Common/ReportHelper.cs @@ -7,95 +7,135 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { - public class ReportHelper - { - /// <summary> Gets java script localized string. </summary> - /// <param name="phrase"> The phrase. </param> - /// <returns> The java script localized string. </returns> - public static string GetJavaScriptLocalizedString(string phrase) - { - var dictionary = BaseItem.LocalizationManager.GetJavaScriptLocalizationDictionary(BaseItem.ConfigurationManager.Configuration.UICulture); - - string value; - - if (dictionary.TryGetValue(phrase, out value)) - { - return value; - } - - return phrase; - } - - /// <summary> Gets server localized string. </summary> - /// <param name="phrase"> The phrase. </param> - /// <returns> The server localized string. </returns> - public static string GetServerLocalizedString(string phrase) - { - return BaseItem.LocalizationManager.GetLocalizedString(phrase, BaseItem.ConfigurationManager.Configuration.UICulture); - } - - /// <summary> Gets row type. </summary> - /// <param name="rowType"> The type. </param> - /// <returns> The row type. </returns> - public static ReportViewType GetRowType(string rowType) - { - if (string.IsNullOrEmpty(rowType)) - return ReportViewType.BaseItem; - - ReportViewType rType; - - if (!Enum.TryParse<ReportViewType>(rowType, out rType)) - return ReportViewType.BaseItem; - - return rType; - } - - /// <summary> Gets header metadata type. </summary> - /// <param name="header"> The header. </param> - /// <returns> The header metadata type. </returns> - public static HeaderMetadata GetHeaderMetadataType(string header) - { - if (string.IsNullOrEmpty(header)) - return HeaderMetadata.None; - - HeaderMetadata rType; - - if (!Enum.TryParse<HeaderMetadata>(header, out rType)) - return HeaderMetadata.None; - - return rType; - } - - /// <summary> Convert field to string. </summary> - /// <typeparam name="T"> Generic type parameter. </typeparam> - /// <param name="value"> The value. </param> - /// <param name="fieldType"> Type of the field. </param> - /// <returns> The field converted to string. </returns> - public static string ConvertToString<T>(T value, ReportFieldType fieldType) - { - if (value == null) - return ""; - switch (fieldType) - { - case ReportFieldType.String: - return value.ToString(); - case ReportFieldType.Boolean: - return value.ToString(); - case ReportFieldType.Date: - return string.Format("{0:d}", value); - case ReportFieldType.Time: - return string.Format("{0:t}", value); - case ReportFieldType.DateTime: - return string.Format("{0:d}", value); + /// <summary> A report helper. </summary> + public class ReportHelper + { + #region [Public Methods] + + /// <summary> Convert field to string. </summary> + /// <typeparam name="T"> Generic type parameter. </typeparam> + /// <param name="value"> The value. </param> + /// <param name="fieldType"> Type of the field. </param> + /// <returns> The field converted to string. </returns> + public static string ConvertToString<T>(T value, ReportFieldType fieldType) + { + if (value == null) + return ""; + switch (fieldType) + { + case ReportFieldType.String: + return value.ToString(); + case ReportFieldType.Boolean: + return value.ToString(); + case ReportFieldType.Date: + return string.Format("{0:d}", value); + case ReportFieldType.Time: + return string.Format("{0:t}", value); + case ReportFieldType.DateTime: + return string.Format("{0:d}", value); case ReportFieldType.Minutes: return string.Format("{0}mn", value); - case ReportFieldType.Int: - return string.Format("", value); - default: - if (value is Guid) - return string.Format("{0:N}", value); - return value.ToString(); - } - } - } + case ReportFieldType.Int: + return string.Format("", value); + default: + if (value is Guid) + return string.Format("{0:N}", value); + return value.ToString(); + } + } + + /// <summary> Gets filtered report header metadata. </summary> + /// <param name="reportColumns"> The report columns. </param> + /// <param name="defaultReturnValue"> The default return value. </param> + /// <returns> The filtered report header metadata. </returns> + public static List<HeaderMetadata> GetFilteredReportHeaderMetadata(string reportColumns, Func<List<HeaderMetadata>> defaultReturnValue = null) + { + if (!string.IsNullOrEmpty(reportColumns)) + { + var s = reportColumns.Split('|').Select(x => ReportHelper.GetHeaderMetadataType(x)).Where(x => x != HeaderMetadata.None); + return s.ToList(); + } + else + if (defaultReturnValue != null) + return defaultReturnValue(); + else + return new List<HeaderMetadata>(); + } + + /// <summary> Gets header metadata type. </summary> + /// <param name="header"> The header. </param> + /// <returns> The header metadata type. </returns> + public static HeaderMetadata GetHeaderMetadataType(string header) + { + if (string.IsNullOrEmpty(header)) + return HeaderMetadata.None; + + HeaderMetadata rType; + + if (!Enum.TryParse<HeaderMetadata>(header, out rType)) + return HeaderMetadata.None; + + return rType; + } + + /// <summary> Gets java script localized string. </summary> + /// <param name="phrase"> The phrase. </param> + /// <returns> The java script localized string. </returns> + public static string GetJavaScriptLocalizedString(string phrase) + { + var dictionary = BaseItem.LocalizationManager.GetJavaScriptLocalizationDictionary(BaseItem.ConfigurationManager.Configuration.UICulture); + + string value; + + if (dictionary.TryGetValue(phrase, out value)) + { + return value; + } + + return phrase; + } + + /// <summary> Gets report view type. </summary> + /// <param name="rowType"> The type. </param> + /// <returns> The report view type. </returns> + public static ReportViewType GetReportViewType(string rowType) + { + if (string.IsNullOrEmpty(rowType)) + return ReportViewType.ReportData; + + ReportViewType rType; + + if (!Enum.TryParse<ReportViewType>(rowType, out rType)) + return ReportViewType.ReportData; + + return rType; + } + + /// <summary> Gets row type. </summary> + /// <param name="rowType"> The type. </param> + /// <returns> The row type. </returns> + public static ReportIncludeItemTypes GetRowType(string rowType) + { + if (string.IsNullOrEmpty(rowType)) + return ReportIncludeItemTypes.BaseItem; + + ReportIncludeItemTypes rType; + + if (!Enum.TryParse<ReportIncludeItemTypes>(rowType, out rType)) + return ReportIncludeItemTypes.BaseItem; + + return rType; + } + + /// <summary> Gets server localized string. </summary> + /// <param name="phrase"> The phrase. </param> + /// <returns> The server localized string. </returns> + public static string GetServerLocalizedString(string phrase) + { + return BaseItem.LocalizationManager.GetLocalizedString(phrase, BaseItem.ConfigurationManager.Configuration.UICulture); + } + + #endregion + + } } diff --git a/MediaBrowser.Api/Reports/Common/ReportIncludeItemTypes.cs b/MediaBrowser.Api/Reports/Common/ReportIncludeItemTypes.cs new file mode 100644 index 000000000..4b6d96792 --- /dev/null +++ b/MediaBrowser.Api/Reports/Common/ReportIncludeItemTypes.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Api.Reports +{ + public enum ReportIncludeItemTypes + { + MusicArtist, + MusicAlbum, + Book, + BoxSet, + Episode, + Game, + Video, + Movie, + MusicVideo, + Trailer, + Season, + Series, + Audio, + BaseItem, + Artist + } +} diff --git a/MediaBrowser.Api/Reports/Common/ReportViewType.cs b/MediaBrowser.Api/Reports/Common/ReportViewType.cs index efdfcb0e7..d9b049bb7 100644 --- a/MediaBrowser.Api/Reports/Common/ReportViewType.cs +++ b/MediaBrowser.Api/Reports/Common/ReportViewType.cs @@ -6,20 +6,9 @@ namespace MediaBrowser.Api.Reports { public enum ReportViewType { - MusicArtist, - MusicAlbum, - Book, - BoxSet, - Episode, - Game, - Video, - Movie, - MusicVideo, - Trailer, - Season, - Series, - Audio, - BaseItem, - Artist + ReportData, + ReportStatistics, + ReportActivities + } } diff --git a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs index 00ce18317..6232018f1 100644 --- a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs +++ b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs @@ -17,162 +17,89 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { - /// <summary> A report builder. </summary> - /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/> - public class ReportBuilder : ReportBuilderBase - { - - /// <summary> - /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilder class. </summary> - /// <param name="libraryManager"> Manager for library. </param> - public ReportBuilder(ILibraryManager libraryManager) - : base(libraryManager) - { - } - - private Func<bool, string> GetBoolString = s => s == true ? "x" : ""; - - public ReportResult GetReportResult(BaseItem[] items, ReportViewType reportRowType, BaseReportRequest request) - { - List<HeaderMetadata> headersMetadata = this.GetFilteredReportHeaderMetadata(reportRowType, request); - - var headers = GetReportHeaders(reportRowType, headersMetadata); - var rows = GetReportRows(items, headersMetadata); - - ReportResult result = new ReportResult { Headers = headers }; - HeaderMetadata groupBy = ReportHelper.GetHeaderMetadataType(request.GroupBy); - int i = headers.FindIndex(x => x.FieldName == groupBy); - if (groupBy != HeaderMetadata.None && i > 0) - { - var rowsGroup = rows.SelectMany(x => x.Columns[i].Name.Split(';'), (x, g) => new { Genre = g.Trim(), Rows = x }) - .GroupBy(x => x.Genre) - .OrderBy(x => x.Key) - .Select(x => new ReportGroup { Name = x.Key, Rows = x.Select(r => r.Rows).ToList() }); - - result.Groups = rowsGroup.ToList(); - result.IsGrouped = true; - } - else - { - result.Rows = rows; - result.IsGrouped = false; - } - - return result; - } - - public List<ReportHeader> GetReportHeaders(ReportViewType reportRowType, BaseReportRequest request) - { - List<ReportHeader> headersMetadata = this.GetReportHeaders(reportRowType); - if (request != null && !string.IsNullOrEmpty(request.ReportColumns)) - { - List<HeaderMetadata> headersMetadataFiltered = this.GetFilteredReportHeaderMetadata(reportRowType, request); - foreach (ReportHeader reportHeader in headersMetadata) - { - if (!headersMetadataFiltered.Contains(reportHeader.FieldName)) - { - reportHeader.Visible = false; - } - } - - - } - - return headersMetadata; - } - - public List<ReportHeader> GetReportHeaders(ReportViewType reportRowType, List<HeaderMetadata> headersMetadata = null) - { - if (headersMetadata == null) - headersMetadata = this.GetDefaultReportHeaderMetadata(reportRowType); - - List<ReportOptions<BaseItem>> options = new List<ReportOptions<BaseItem>>(); - foreach (HeaderMetadata header in headersMetadata) - { - options.Add(GetReportOption(header)); - } - - - List<ReportHeader> headers = new List<ReportHeader>(); - foreach (ReportOptions<BaseItem> option in options) - { - headers.Add(option.Header); - } - return headers; - } - - private List<ReportRow> GetReportRows(IEnumerable<BaseItem> items, List<HeaderMetadata> headersMetadata) - { - List<ReportOptions<BaseItem>> options = new List<ReportOptions<BaseItem>>(); - foreach (HeaderMetadata header in headersMetadata) - { - options.Add(GetReportOption(header)); - } - - var rows = new List<ReportRow>(); - - foreach (BaseItem item in items) - { - ReportRow rRow = GetRow(item); - foreach (ReportOptions<BaseItem> option in options) - { - object itemColumn = option.Column != null ? option.Column(item, rRow) : ""; - object itemId = option.ItemID != null ? option.ItemID(item) : ""; - ReportItem rItem = new ReportItem - { - Name = ReportHelper.ConvertToString(itemColumn, option.Header.HeaderFieldType), - Id = ReportHelper.ConvertToString(itemId, ReportFieldType.Object) - }; - rRow.Columns.Add(rItem); - } - - rows.Add(rRow); - } - - return rows; - } - - /// <summary> Gets a row. </summary> - /// <param name="item"> The item. </param> - /// <returns> The row. </returns> - private ReportRow GetRow(BaseItem item) - { - var hasTrailers = item as IHasTrailers; - var hasSpecialFeatures = item as IHasSpecialFeatures; - var video = item as Video; - ReportRow rRow = new ReportRow - { - Id = item.Id.ToString("N"), - HasLockData = item.IsLocked, - IsUnidentified = item.IsUnidentified, - HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false, - HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0), - HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0), - HasImageTagsLogo = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Logo) > 0), - HasSpecials = hasSpecialFeatures != null ? hasSpecialFeatures.SpecialFeatureIds.Count > 0 : false, - HasSubtitles = video != null ? video.HasSubtitles : false, - RowType = ReportHelper.GetRowType(item.GetClientTypeName()) - }; - return rRow; - } - public List<HeaderMetadata> GetFilteredReportHeaderMetadata(ReportViewType reportRowType, BaseReportRequest request) - { - if (request != null && !string.IsNullOrEmpty(request.ReportColumns)) - { - var s = request.ReportColumns.Split('|').Select(x => ReportHelper.GetHeaderMetadataType(x)).Where(x => x != HeaderMetadata.None); - return s.ToList(); - } - else - return this.GetDefaultReportHeaderMetadata(reportRowType); - - } - - public List<HeaderMetadata> GetDefaultReportHeaderMetadata(ReportViewType reportRowType) - { - switch (reportRowType) - { - case ReportViewType.Season: - return new List<HeaderMetadata> + /// <summary> A report builder. </summary> + /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/> + public class ReportBuilder : ReportBuilderBase + { + + #region [Constructors] + + /// <summary> + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilder class. </summary> + /// <param name="libraryManager"> Manager for library. </param> + public ReportBuilder(ILibraryManager libraryManager) + : base(libraryManager) + { + } + + #endregion + + #region [Public Methods] + + /// <summary> Gets report result. </summary> + /// <param name="items"> The items. </param> + /// <param name="request"> The request. </param> + /// <returns> The report result. </returns> + public ReportResult GetResult(BaseItem[] items, IReportsQuery request) + { + ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + List<ReportOptions<BaseItem>> options = this.GetReportOptions<BaseItem>(request, + () => this.GetDefaultHeaderMetadata(reportRowType), + (hm) => this.GetOption(hm)).Where(x => x.Header.Visible == true).ToList(); + + var headers = GetHeaders<BaseItem>(options); + var rows = GetReportRows(items, options); + + ReportResult result = new ReportResult { Headers = headers }; + HeaderMetadata groupBy = ReportHelper.GetHeaderMetadataType(request.GroupBy); + int i = headers.FindIndex(x => x.FieldName == groupBy); + if (groupBy != HeaderMetadata.None && i >= 0) + { + var rowsGroup = rows.SelectMany(x => x.Columns[i].Name.Split(';'), (x, g) => new { Group = g.Trim(), Rows = x }) + .GroupBy(x => x.Group) + .OrderBy(x => x.Key) + .Select(x => new ReportGroup { Name = x.Key, Rows = x.Select(r => r.Rows).ToList() }); + + result.Groups = rowsGroup.ToList(); + result.IsGrouped = true; + } + else + { + result.Rows = rows; + result.IsGrouped = false; + } + + return result; + } + + #endregion + + #region [Protected Internal Methods] + + /// <summary> Gets the headers. </summary> + /// <typeparam name="H"> Type of the header. </typeparam> + /// <param name="request"> The request. </param> + /// <returns> The headers. </returns> + /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/> + protected internal override List<ReportHeader> GetHeaders<H>(H request) + { + ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + return this.GetHeaders<BaseItem>(request, () => this.GetDefaultHeaderMetadata(reportRowType), (hm) => this.GetOption(hm)); + } + + #endregion + + #region [Private Methods] + + /// <summary> Gets default report header metadata. </summary> + /// <param name="reportIncludeItemTypes"> Type of the report row. </param> + /// <returns> The default report header metadata. </returns> + private List<HeaderMetadata> GetDefaultHeaderMetadata(ReportIncludeItemTypes reportIncludeItemTypes) + { + switch (reportIncludeItemTypes) + { + case ReportIncludeItemTypes.Season: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Series, @@ -183,8 +110,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Genres }; - case ReportViewType.Series: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Series: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -199,8 +126,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Specials }; - case ReportViewType.MusicAlbum: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.MusicAlbum: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -212,8 +139,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Genres }; - case ReportViewType.MusicArtist: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.MusicArtist: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.MusicArtist, @@ -223,8 +150,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Genres }; - case ReportViewType.Game: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Game: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -239,8 +166,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Trailers }; - case ReportViewType.Movie: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Movie: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -259,8 +186,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Specials }; - case ReportViewType.Book: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Book: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -272,8 +199,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.CommunityRating }; - case ReportViewType.BoxSet: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.BoxSet: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -286,8 +213,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Trailers }; - case ReportViewType.Audio: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Audio: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -305,8 +232,8 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Audio }; - case ReportViewType.Episode: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Episode: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -327,12 +254,12 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Specials }; - case ReportViewType.Video: - case ReportViewType.MusicVideo: - case ReportViewType.Trailer: - case ReportViewType.BaseItem: - default: - return new List<HeaderMetadata> + case ReportIncludeItemTypes.Video: + case ReportIncludeItemTypes.MusicVideo: + case ReportIncludeItemTypes.Trailer: + case ReportIncludeItemTypes.BaseItem: + default: + return new List<HeaderMetadata> { HeaderMetadata.StatusImage, HeaderMetadata.Name, @@ -351,239 +278,282 @@ namespace MediaBrowser.Api.Reports HeaderMetadata.Specials }; - } - - } - - /// <summary> Gets report option. </summary> - /// <param name="header"> The header. </param> - /// <param name="sortField"> The sort field. </param> - /// <returns> The report option. </returns> - private ReportOptions<BaseItem> GetReportOption(HeaderMetadata header, string sortField = "") - { - ReportHeader reportHeader = new ReportHeader - { - HeaderFieldType = ReportFieldType.String, - SortField = sortField, - Type = "", - ItemViewType = ItemViewType.None - }; - - Func<BaseItem, ReportRow, object> column = null; - Func<BaseItem, object> itemId = null; - HeaderMetadata internalHeader = header; - - switch (header) - { - case HeaderMetadata.StatusImage: - reportHeader.ItemViewType = ItemViewType.StatusImage; - internalHeader = HeaderMetadata.Status; - reportHeader.CanGroup = false; - break; - - case HeaderMetadata.Name: - column = (i, r) => i.Name; - reportHeader.ItemViewType = ItemViewType.Detail; - reportHeader.SortField = "SortName"; - break; - - case HeaderMetadata.DateAdded: - column = (i, r) => i.DateCreated; - reportHeader.SortField = "DateCreated,SortName"; - reportHeader.HeaderFieldType = ReportFieldType.DateTime; - reportHeader.Type = ""; - break; - - case HeaderMetadata.PremiereDate: - case HeaderMetadata.ReleaseDate: - column = (i, r) => i.PremiereDate; - reportHeader.HeaderFieldType = ReportFieldType.DateTime; - reportHeader.SortField = "ProductionYear,PremiereDate,SortName"; - break; - - case HeaderMetadata.Runtime: - column = (i, r) => this.GetRuntimeDateTime(i.RunTimeTicks); - reportHeader.HeaderFieldType = ReportFieldType.Minutes; - reportHeader.SortField = "Runtime,SortName"; - break; - - case HeaderMetadata.PlayCount: - reportHeader.HeaderFieldType = ReportFieldType.Int; - break; - - case HeaderMetadata.Season: - column = (i, r) => this.GetEpisode(i); - reportHeader.ItemViewType = ItemViewType.Detail; - reportHeader.SortField = "SortName"; - break; - - case HeaderMetadata.SeasonNumber: - column = (i, r) => this.GetObject<Season, string>(i, (x) => x.IndexNumber == null ? "" : x.IndexNumber.ToString()); - reportHeader.SortField = "IndexNumber"; - reportHeader.HeaderFieldType = ReportFieldType.Int; - break; - - case HeaderMetadata.Series: - column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName); - reportHeader.ItemViewType = ItemViewType.Detail; - reportHeader.SortField = "SeriesSortName,SortName"; - break; - - case HeaderMetadata.EpisodeSeries: - column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName); - reportHeader.ItemViewType = ItemViewType.Detail; - itemId = (i) => - { - Series series = this.GetObject<Episode, Series>(i, (x) => x.Series); - if (series == null) - return string.Empty; - return series.Id; - }; - reportHeader.SortField = "SeriesSortName,SortName"; - internalHeader = HeaderMetadata.Series; - break; - - case HeaderMetadata.EpisodeSeason: - column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName); - reportHeader.ItemViewType = ItemViewType.Detail; - itemId = (i) => - { - Season season = this.GetObject<Episode, Season>(i, (x) => x.Season); - if (season == null) - return string.Empty; - return season.Id; - }; - reportHeader.SortField = "SortName"; - internalHeader = HeaderMetadata.Season; - break; - - case HeaderMetadata.Network: - column = (i, r) => this.GetListAsString(i.Studios); - itemId = (i) => this.GetStudioID(i.Studios.FirstOrDefault()); - reportHeader.ItemViewType = ItemViewType.ItemByNameDetails; - reportHeader.SortField = "Studio,SortName"; - break; - - case HeaderMetadata.Year: - column = (i, r) => this.GetSeriesProductionYear(i); - reportHeader.SortField = "ProductionYear,PremiereDate,SortName"; - break; - - case HeaderMetadata.ParentalRating: - column = (i, r) => i.OfficialRating; - reportHeader.SortField = "OfficialRating,SortName"; - break; - - case HeaderMetadata.CommunityRating: - column = (i, r) => i.CommunityRating; - reportHeader.SortField = "CommunityRating,SortName"; - break; - - case HeaderMetadata.Trailers: - column = (i, r) => this.GetBoolString(r.HasLocalTrailer); - reportHeader.ItemViewType = ItemViewType.TrailersImage; - break; - - case HeaderMetadata.Specials: - column = (i, r) => this.GetBoolString(r.HasSpecials); - reportHeader.ItemViewType = ItemViewType.SpecialsImage; - break; - - case HeaderMetadata.GameSystem: - column = (i, r) => this.GetObject<Game, string>(i, (x) => x.GameSystem); - reportHeader.SortField = "GameSystem,SortName"; - break; - - case HeaderMetadata.Players: - column = (i, r) => this.GetObject<Game, int?>(i, (x) => x.PlayersSupported); - reportHeader.SortField = "Players,GameSystem,SortName"; - break; - - case HeaderMetadata.AlbumArtist: - column = (i, r) => this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist); - itemId = (i) => this.GetPersonID(this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist)); - reportHeader.ItemViewType = ItemViewType.Detail; - reportHeader.SortField = "AlbumArtist,Album,SortName"; - - break; - case HeaderMetadata.MusicArtist: - column = (i, r) => this.GetObject<MusicArtist, string>(i, (x) => x.GetLookupInfo().Name); - reportHeader.ItemViewType = ItemViewType.Detail; - reportHeader.SortField = "AlbumArtist,Album,SortName"; - internalHeader = HeaderMetadata.AlbumArtist; - break; - case HeaderMetadata.AudioAlbumArtist: - column = (i, r) => this.GetListAsString(this.GetObject<Audio, List<string>>(i, (x) => x.AlbumArtists)); - reportHeader.SortField = "AlbumArtist,Album,SortName"; - internalHeader = HeaderMetadata.AlbumArtist; - break; - - case HeaderMetadata.AudioAlbum: - column = (i, r) => this.GetObject<Audio, string>(i, (x) => x.Album); - reportHeader.SortField = "Album,SortName"; - internalHeader = HeaderMetadata.Album; - break; - - case HeaderMetadata.Countries: - column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations)); - break; - - case HeaderMetadata.Disc: - column = (i, r) => i.ParentIndexNumber; - break; - - case HeaderMetadata.Track: - column = (i, r) => i.IndexNumber; - break; - - case HeaderMetadata.Tracks: - column = (i, r) => this.GetObject<MusicAlbum, List<Audio>>(i, (x) => x.Tracks.ToList(), new List<Audio>()).Count(); - break; - - case HeaderMetadata.Audio: - column = (i, r) => this.GetAudioStream(i); - break; - - case HeaderMetadata.EmbeddedImage: - break; - - case HeaderMetadata.Video: - column = (i, r) => this.GetVideoStream(i); - break; - - case HeaderMetadata.Resolution: - column = (i, r) => this.GetVideoResolution(i); - break; - - case HeaderMetadata.Subtitles: - column = (i, r) => this.GetBoolString(r.HasSubtitles); - reportHeader.ItemViewType = ItemViewType.SubtitleImage; - break; - - case HeaderMetadata.Genres: - column = (i, r) => this.GetListAsString(i.Genres); - break; - - } - - string headerName = ""; - if (internalHeader != HeaderMetadata.None) - { - string localHeader = "Header" + internalHeader.ToString(); - headerName = internalHeader != HeaderMetadata.None ? ReportHelper.GetJavaScriptLocalizedString(localHeader) : ""; - if (string.Compare(localHeader, headerName, StringComparison.CurrentCultureIgnoreCase) == 0) - headerName = ReportHelper.GetServerLocalizedString(localHeader); - } - - reportHeader.Name = headerName; - reportHeader.FieldName = header; - ReportOptions<BaseItem> option = new ReportOptions<BaseItem>() - { - Header = reportHeader, - Column = column, - ItemID = itemId - }; - return option; - } - } + } + + } + + /// <summary> Gets report option. </summary> + /// <param name="header"> The header. </param> + /// <param name="sortField"> The sort field. </param> + /// <returns> The report option. </returns> + private ReportOptions<BaseItem> GetOption(HeaderMetadata header, string sortField = "") + { + HeaderMetadata internalHeader = header; + + ReportOptions<BaseItem> option = new ReportOptions<BaseItem>() + { + Header = new ReportHeader + { + HeaderFieldType = ReportFieldType.String, + SortField = sortField, + Type = "", + ItemViewType = ItemViewType.None + } + }; + + switch (header) + { + case HeaderMetadata.StatusImage: + option.Header.ItemViewType = ItemViewType.StatusImage; + internalHeader = HeaderMetadata.Status; + option.Header.CanGroup = false; + break; + + case HeaderMetadata.Name: + option.Column = (i, r) => i.Name; + option.Header.ItemViewType = ItemViewType.Detail; + option.Header.SortField = "SortName"; + break; + + case HeaderMetadata.DateAdded: + option.Column = (i, r) => i.DateCreated; + option.Header.SortField = "DateCreated,SortName"; + option.Header.HeaderFieldType = ReportFieldType.DateTime; + option.Header.Type = ""; + break; + + case HeaderMetadata.PremiereDate: + case HeaderMetadata.ReleaseDate: + option.Column = (i, r) => i.PremiereDate; + option.Header.HeaderFieldType = ReportFieldType.DateTime; + option.Header.SortField = "ProductionYear,PremiereDate,SortName"; + break; + + case HeaderMetadata.Runtime: + option.Column = (i, r) => this.GetRuntimeDateTime(i.RunTimeTicks); + option.Header.HeaderFieldType = ReportFieldType.Minutes; + option.Header.SortField = "Runtime,SortName"; + break; + + case HeaderMetadata.PlayCount: + option.Header.HeaderFieldType = ReportFieldType.Int; + break; + + case HeaderMetadata.Season: + option.Column = (i, r) => this.GetEpisode(i); + option.Header.ItemViewType = ItemViewType.Detail; + option.Header.SortField = "SortName"; + break; + + case HeaderMetadata.SeasonNumber: + option.Column = (i, r) => this.GetObject<Season, string>(i, (x) => x.IndexNumber == null ? "" : x.IndexNumber.ToString()); + option.Header.SortField = "IndexNumber"; + option.Header.HeaderFieldType = ReportFieldType.Int; + break; + + case HeaderMetadata.Series: + option.Column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName); + option.Header.ItemViewType = ItemViewType.Detail; + option.Header.SortField = "SeriesSortName,SortName"; + break; + + case HeaderMetadata.EpisodeSeries: + option.Column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName); + option.Header.ItemViewType = ItemViewType.Detail; + option.ItemID = (i) => + { + Series series = this.GetObject<Episode, Series>(i, (x) => x.Series); + if (series == null) + return string.Empty; + return series.Id; + }; + option.Header.SortField = "SeriesSortName,SortName"; + internalHeader = HeaderMetadata.Series; + break; + + case HeaderMetadata.EpisodeSeason: + option.Column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName); + option.Header.ItemViewType = ItemViewType.Detail; + option.ItemID = (i) => + { + Season season = this.GetObject<Episode, Season>(i, (x) => x.Season); + if (season == null) + return string.Empty; + return season.Id; + }; + option.Header.SortField = "SortName"; + internalHeader = HeaderMetadata.Season; + break; + + case HeaderMetadata.Network: + option.Column = (i, r) => this.GetListAsString(i.Studios); + option.ItemID = (i) => this.GetStudioID(i.Studios.FirstOrDefault()); + option.Header.ItemViewType = ItemViewType.ItemByNameDetails; + option.Header.SortField = "Studio,SortName"; + break; + + case HeaderMetadata.Year: + option.Column = (i, r) => this.GetSeriesProductionYear(i); + option.Header.SortField = "ProductionYear,PremiereDate,SortName"; + break; + + case HeaderMetadata.ParentalRating: + option.Column = (i, r) => i.OfficialRating; + option.Header.SortField = "OfficialRating,SortName"; + break; + + case HeaderMetadata.CommunityRating: + option.Column = (i, r) => i.CommunityRating; + option.Header.SortField = "CommunityRating,SortName"; + break; + + case HeaderMetadata.Trailers: + option.Column = (i, r) => this.GetBoolString(r.HasLocalTrailer); + option.Header.ItemViewType = ItemViewType.TrailersImage; + break; + + case HeaderMetadata.Specials: + option.Column = (i, r) => this.GetBoolString(r.HasSpecials); + option.Header.ItemViewType = ItemViewType.SpecialsImage; + break; + + case HeaderMetadata.GameSystem: + option.Column = (i, r) => this.GetObject<Game, string>(i, (x) => x.GameSystem); + option.Header.SortField = "GameSystem,SortName"; + break; + + case HeaderMetadata.Players: + option.Column = (i, r) => this.GetObject<Game, int?>(i, (x) => x.PlayersSupported); + option.Header.SortField = "Players,GameSystem,SortName"; + break; + + case HeaderMetadata.AlbumArtist: + option.Column = (i, r) => this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist); + option.ItemID = (i) => this.GetPersonID(this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist)); + option.Header.ItemViewType = ItemViewType.Detail; + option.Header.SortField = "AlbumArtist,Album,SortName"; + + break; + case HeaderMetadata.MusicArtist: + option.Column = (i, r) => this.GetObject<MusicArtist, string>(i, (x) => x.GetLookupInfo().Name); + option.Header.ItemViewType = ItemViewType.Detail; + option.Header.SortField = "AlbumArtist,Album,SortName"; + internalHeader = HeaderMetadata.AlbumArtist; + break; + case HeaderMetadata.AudioAlbumArtist: + option.Column = (i, r) => this.GetListAsString(this.GetObject<Audio, List<string>>(i, (x) => x.AlbumArtists)); + option.Header.SortField = "AlbumArtist,Album,SortName"; + internalHeader = HeaderMetadata.AlbumArtist; + break; + + case HeaderMetadata.AudioAlbum: + option.Column = (i, r) => this.GetObject<Audio, string>(i, (x) => x.Album); + option.Header.SortField = "Album,SortName"; + internalHeader = HeaderMetadata.Album; + break; + + case HeaderMetadata.Countries: + option.Column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations)); + break; + + case HeaderMetadata.Disc: + option.Column = (i, r) => i.ParentIndexNumber; + break; + + case HeaderMetadata.Track: + option.Column = (i, r) => i.IndexNumber; + break; + + case HeaderMetadata.Tracks: + option.Column = (i, r) => this.GetObject<MusicAlbum, List<Audio>>(i, (x) => x.Tracks.ToList(), new List<Audio>()).Count(); + break; + + case HeaderMetadata.Audio: + option.Column = (i, r) => this.GetAudioStream(i); + break; + + case HeaderMetadata.EmbeddedImage: + break; + + case HeaderMetadata.Video: + option.Column = (i, r) => this.GetVideoStream(i); + break; + + case HeaderMetadata.Resolution: + option.Column = (i, r) => this.GetVideoResolution(i); + break; + + case HeaderMetadata.Subtitles: + option.Column = (i, r) => this.GetBoolString(r.HasSubtitles); + option.Header.ItemViewType = ItemViewType.SubtitleImage; + break; + + case HeaderMetadata.Genres: + option.Column = (i, r) => this.GetListAsString(i.Genres); + break; + + } + + option.Header.Name = GetLocalizedHeader(internalHeader); + option.Header.FieldName = header; + + return option; + } + + /// <summary> Gets report rows. </summary> + /// <param name="items"> The items. </param> + /// <param name="options"> Options for controlling the operation. </param> + /// <returns> The report rows. </returns> + private List<ReportRow> GetReportRows(IEnumerable<BaseItem> items, List<ReportOptions<BaseItem>> options) + { + var rows = new List<ReportRow>(); + + foreach (BaseItem item in items) + { + ReportRow rRow = GetRow(item); + foreach (ReportOptions<BaseItem> option in options) + { + object itemColumn = option.Column != null ? option.Column(item, rRow) : ""; + object itemId = option.ItemID != null ? option.ItemID(item) : ""; + ReportItem rItem = new ReportItem + { + Name = ReportHelper.ConvertToString(itemColumn, option.Header.HeaderFieldType), + Id = ReportHelper.ConvertToString(itemId, ReportFieldType.Object) + }; + rRow.Columns.Add(rItem); + } + + rows.Add(rRow); + } + + return rows; + } + + /// <summary> Gets a row. </summary> + /// <param name="item"> The item. </param> + /// <returns> The row. </returns> + private ReportRow GetRow(BaseItem item) + { + var hasTrailers = item as IHasTrailers; + var hasSpecialFeatures = item as IHasSpecialFeatures; + var video = item as Video; + ReportRow rRow = new ReportRow + { + Id = item.Id.ToString("N"), + HasLockData = item.IsLocked, + IsUnidentified = item.IsUnidentified, + HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false, + HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0), + HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0), + HasImageTagsLogo = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Logo) > 0), + HasSpecials = hasSpecialFeatures != null ? hasSpecialFeatures.SpecialFeatureIds.Count > 0 : false, + HasSubtitles = video != null ? video.HasSubtitles : false, + RowType = ReportHelper.GetRowType(item.GetClientTypeName()) + }; + return rRow; + } + + #endregion + + } } diff --git a/MediaBrowser.Api/Reports/Data/ReportOptions.cs b/MediaBrowser.Api/Reports/Data/ReportOptions.cs index aed15d428..b4fd2ee85 100644 --- a/MediaBrowser.Api/Reports/Data/ReportOptions.cs +++ b/MediaBrowser.Api/Reports/Data/ReportOptions.cs @@ -7,8 +7,9 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { + /// <summary> A report options. </summary> - internal class ReportOptions<I> + public class ReportOptions<I> { /// <summary> Initializes a new instance of the ReportOptions class. </summary> public ReportOptions() diff --git a/MediaBrowser.Api/Reports/Data/ReportGroup.cs b/MediaBrowser.Api/Reports/Model/ReportGroup.cs index 49c76c7ba..49c76c7ba 100644 --- a/MediaBrowser.Api/Reports/Data/ReportGroup.cs +++ b/MediaBrowser.Api/Reports/Model/ReportGroup.cs diff --git a/MediaBrowser.Api/Reports/Data/ReportHeader.cs b/MediaBrowser.Api/Reports/Model/ReportHeader.cs index 81b85954a..81b85954a 100644 --- a/MediaBrowser.Api/Reports/Data/ReportHeader.cs +++ b/MediaBrowser.Api/Reports/Model/ReportHeader.cs diff --git a/MediaBrowser.Api/Reports/Data/ReportItem.cs b/MediaBrowser.Api/Reports/Model/ReportItem.cs index 06d0b0c46..06d0b0c46 100644 --- a/MediaBrowser.Api/Reports/Data/ReportItem.cs +++ b/MediaBrowser.Api/Reports/Model/ReportItem.cs diff --git a/MediaBrowser.Api/Reports/Data/ReportResult.cs b/MediaBrowser.Api/Reports/Model/ReportResult.cs index a4bc95aa1..a4bc95aa1 100644 --- a/MediaBrowser.Api/Reports/Data/ReportResult.cs +++ b/MediaBrowser.Api/Reports/Model/ReportResult.cs diff --git a/MediaBrowser.Api/Reports/Data/ReportRow.cs b/MediaBrowser.Api/Reports/Model/ReportRow.cs index f2165344a..d9bae8afa 100644 --- a/MediaBrowser.Api/Reports/Data/ReportRow.cs +++ b/MediaBrowser.Api/Reports/Model/ReportRow.cs @@ -66,6 +66,6 @@ namespace MediaBrowser.Api.Reports /// <summary> Gets or sets the type. </summary> /// <value> The type. </value> - public ReportViewType RowType { get; set; } + public ReportIncludeItemTypes RowType { get; set; } } } diff --git a/MediaBrowser.Api/Reports/ReportRequests.cs b/MediaBrowser.Api/Reports/ReportRequests.cs index 7fe0bfba1..1a7b3284b 100644 --- a/MediaBrowser.Api/Reports/ReportRequests.cs +++ b/MediaBrowser.Api/Reports/ReportRequests.cs @@ -7,8 +7,80 @@ using System.Linq; namespace MediaBrowser.Api.Reports { - public class BaseReportRequest : BaseItemsRequest - { + public interface IReportsDownload : IReportsQuery + { + /// <summary> Gets or sets the minimum date. </summary> + /// <value> The minimum date. </value> + string MinDate { get; set; } + } + + /// <summary> Interface for reports query. </summary> + public interface IReportsQuery : IReportsHeader + { + /// <summary> + /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.GetActivityLogs has + /// query limit. </summary> + /// <value> + /// true if this MediaBrowser.Api.Reports.GetActivityLogs has query limit, false if not. </value> + bool HasQueryLimit { get; set; } + /// <summary> Gets or sets who group this MediaBrowser.Api.Reports.GetActivityLogs. </summary> + /// <value> Describes who group this MediaBrowser.Api.Reports.GetActivityLogs. </value> + string GroupBy { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + int? StartIndex { get; set; } + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + int? Limit { get; set; } + + } + public interface IReportsHeader + { + /// <summary> Gets or sets the report view. </summary> + /// <value> The report view. </value> + string ReportView { get; set; } + + /// <summary> Gets or sets the report columns. </summary> + /// <value> The report columns. </value> + string ReportColumns { get; set; } + + /// <summary> Gets or sets a list of types of the include items. </summary> + /// <value> A list of types of the include items. </value> + string IncludeItemTypes { get; set; } + + } + + public class BaseReportRequest : BaseItemsRequest, IReportsQuery + { + /// <summary> Gets or sets the report view. </summary> + /// <value> The report view. </value> + [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ReportView { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.BaseReportRequest has + /// query limit. </summary> + /// <value> + /// true if this MediaBrowser.Api.Reports.BaseReportRequest has query limit, false if not. </value> + [ApiMember(Name = "HasQueryLimit", Description = "Optional. If specified, results will include all records.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool HasQueryLimit { get; set; } + + /// <summary> + /// Gets or sets who group this MediaBrowser.Api.Reports.BaseReportRequest. </summary> + /// <value> Describes who group this MediaBrowser.Api.Reports.BaseReportRequest. </value> + [ApiMember(Name = "GroupBy", Description = "Optional. If specified, results will include grouped records.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string GroupBy { get; set; } + + /// <summary> Gets or sets the report columns. </summary> + /// <value> The report columns. </value> + [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ReportColumns { get; set; } + /// <summary> /// Gets or sets the user id. /// </summary> @@ -63,11 +135,6 @@ namespace MediaBrowser.Api.Reports [ApiMember(Name = "Ids", Description = "Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Ids { get; set; } - public bool HasQueryLimit { get; set; } - public string GroupBy { get; set; } - - public string ReportColumns { get; set; } - /// <summary> /// Gets or sets the video types. /// </summary> @@ -261,8 +328,22 @@ namespace MediaBrowser.Api.Reports } [Route("/Reports/Headers", "GET", Summary = "Gets reports headers based on library items")] - public class GetReportHeaders : BaseReportRequest, IReturn<List<ReportHeader>> - { + public class GetReportHeaders : IReturn<List<ReportHeader>>, IReportsHeader + { + /// <summary> Gets or sets the report view. </summary> + /// <value> The report view. </value> + [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ReportView { get; set; } + + /// <summary> Gets or sets a list of types of the include items. </summary> + /// <value> A list of types of the include items. </value> + [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string IncludeItemTypes { get; set; } + + /// <summary> Gets or sets the report columns. </summary> + /// <value> The report columns. </value> + [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ReportColumns { get; set; } } [Route("/Reports/Statistics", "GET", Summary = "Gets reports statistics based on library items")] @@ -273,7 +354,7 @@ namespace MediaBrowser.Api.Reports } [Route("/Reports/Items/Download", "GET", Summary = "Downloads report")] - public class GetReportDownload : BaseReportRequest + public class GetReportDownload : BaseReportRequest, IReportsDownload { public GetReportDownload() { @@ -281,6 +362,61 @@ namespace MediaBrowser.Api.Reports } public ReportExportType ExportType { get; set; } + + /// <summary> Gets or sets the minimum date. </summary> + /// <value> The minimum date. </value> + [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string MinDate { get; set; } + } + + [Route("/Reports/Activities", "GET", Summary = "Gets activities entries")] + public class GetActivityLogs : IReturn<ReportResult>, IReportsQuery, IReportsDownload + { + /// <summary> Gets or sets the report view. </summary> + /// <value> The report view. </value> + [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ReportView { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.GetActivityLogs has + /// query limit. </summary> + /// <value> + /// true if this MediaBrowser.Api.Reports.GetActivityLogs has query limit, false if not. </value> + [ApiMember(Name = "HasQueryLimit", Description = "Optional. If specified, results will include all records.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool HasQueryLimit { get; set; } + + /// <summary> Gets or sets who group this MediaBrowser.Api.Reports.GetActivityLogs. </summary> + /// <value> Describes who group this MediaBrowser.Api.Reports.GetActivityLogs. </value> + [ApiMember(Name = "GroupBy", Description = "Optional. If specified, results will include grouped records.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string GroupBy { get; set; } + + /// <summary> Gets or sets the report columns. </summary> + /// <value> The report columns. </value> + [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ReportColumns { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } + + /// <summary> Gets or sets the minimum date. </summary> + /// <value> The minimum date. </value> + [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string MinDate { get; set; } + + [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string IncludeItemTypes { get; set; } + } } diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 4438876f7..2c0728719 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -24,1140 +24,1219 @@ using System.Text; namespace MediaBrowser.Api.Reports { - /// <summary> The reports service. </summary> - /// <seealso cref="T:MediaBrowser.Api.BaseApiService"/> - public class ReportsService : BaseApiService - { - - - /// <summary> Manager for user. </summary> - private readonly IUserManager _userManager; - - /// <summary> Manager for library. </summary> - private readonly ILibraryManager _libraryManager; - /// <summary> The localization. </summary> - private readonly ILocalizationManager _localization; - - /// <summary> - /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class. </summary> - /// <param name="userManager"> Manager for user. </param> - /// <param name="libraryManager"> Manager for library. </param> - /// <param name="localization"> The localization. </param> - public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization) - { - _userManager = userManager; - _libraryManager = libraryManager; - _localization = localization; - } - - /// <summary> Gets the given request. </summary> - /// <param name="request"> The request. </param> - /// <returns> A Task<object> </returns> - public async Task<object> Get(GetReportHeaders request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; - - ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); - var reportResult = reportBuilder.GetReportHeaders(reportRowType, request); - - return ToOptimizedResult(reportResult); - - } - - /// <summary> Gets the given request. </summary> - /// <param name="request"> The request. </param> - /// <returns> A Task<object> </returns> - public async Task<object> Get(GetItemReport request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; - - var reportResult = await GetReportResult(request); - - return ToOptimizedResult(reportResult); - } - - /// <summary> Gets the given request. </summary> - /// <param name="request"> The request. </param> - /// <returns> A Task<object> </returns> - public async Task<object> Get(GetReportDownload request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; - - var headers = new Dictionary<string, string>(); - string fileExtension = "csv"; - string contentType = "text/plain;charset='utf-8'"; - - switch (request.ExportType) - { - case ReportExportType.CSV: - break; - case ReportExportType.Excel: - contentType = "application/vnd.ms-excel"; - fileExtension = "xls"; - break; - } - - var filename = "ReportExport." + fileExtension; - headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename); - headers["Content-Encoding"] = "UTF-8"; - - ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); - QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false); - ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request); - - reportResult.TotalRecordCount = queryResult.TotalRecordCount; - - string result = string.Empty; - switch (request.ExportType) - { - case ReportExportType.CSV: - result = new ReportExport().ExportToCsv(reportResult); - break; - case ReportExportType.Excel: - result = new ReportExport().ExportToExcel(reportResult); - break; - } - - object ro = ResultFactory.GetResult(result, contentType, headers); - return ro; - } - - /// <summary> Gets the given request. </summary> - /// <param name="request"> The request. </param> - /// <returns> A Task<object> </returns> - public async Task<object> Get(GetReportStatistics request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; - var reportResult = await GetReportStatistic(request); - - return ToOptimizedResult(reportResult); - } - - /// <summary> Gets report statistic. </summary> - /// <param name="request"> The request. </param> - /// <returns> The report statistic. </returns> - private async Task<ReportStatResult> GetReportStatistic(GetReportStatistics request) - { - ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false); - - ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager); - ReportStatResult reportResult = reportBuilder.GetReportStatResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5); - reportResult.TotalRecordCount = reportResult.Groups.Count(); - return reportResult; - } - - /// <summary> Gets report result. </summary> - /// <param name="request"> The request. </param> - /// <returns> The report result. </returns> - private async Task<ReportResult> GetReportResult(GetItemReport request) - { - - ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); - QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false); - ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request); - reportResult.TotalRecordCount = queryResult.TotalRecordCount; - - return reportResult; - } - - /// <summary> Gets query result. </summary> - /// <param name="request"> The request. </param> - /// <returns> The query result. </returns> - private async Task<QueryResult<BaseItem>> GetQueryResult(BaseReportRequest request) - { - // Placeholder in case needed later - request.Recursive = true; - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts"; - - var parentItem = string.IsNullOrEmpty(request.ParentId) ? - (user == null ? _libraryManager.RootFolder : user.RootFolder) : - _libraryManager.GetItemById(request.ParentId); - - var item = string.IsNullOrEmpty(request.ParentId) ? - user == null ? _libraryManager.RootFolder : user.RootFolder : - parentItem; - - IEnumerable<BaseItem> items; - - if (request.Recursive) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); - return result; - } - else - { - if (user == null) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); - return result; - } - - var userRoot = item as UserRootFolder; - - if (userRoot == null) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); - - return result; - } - - items = ((Folder)item).GetChildren(user, true); - } - - return new QueryResult<BaseItem> { Items = items.ToArray() }; - - } - - /// <summary> Gets items query. </summary> - /// <param name="request"> The request. </param> - /// <param name="user"> The user. </param> - /// <returns> The items query. </returns> - private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user) - { - var query = new InternalItemsQuery - { - User = user, - IsPlayed = request.IsPlayed, - MediaTypes = request.GetMediaTypes(), - IncludeItemTypes = request.GetIncludeItemTypes(), - ExcludeItemTypes = request.GetExcludeItemTypes(), - Recursive = true, - SortBy = request.GetOrderBy(), - SortOrder = request.SortOrder ?? SortOrder.Ascending, - - Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager), - StartIndex = request.StartIndex, - IsMissing = request.IsMissing, - IsVirtualUnaired = request.IsVirtualUnaired, - IsUnaired = request.IsUnaired, - CollapseBoxSetItems = request.CollapseBoxSetItems, - NameLessThan = request.NameLessThan, - NameStartsWith = request.NameStartsWith, - NameStartsWithOrGreater = request.NameStartsWithOrGreater, - HasImdbId = request.HasImdbId, - IsYearMismatched = request.IsYearMismatched, - IsUnidentified = request.IsUnidentified, - IsPlaceHolder = request.IsPlaceHolder, - IsLocked = request.IsLocked, - IsInBoxSet = request.IsInBoxSet, - IsHD = request.IsHD, - Is3D = request.Is3D, - HasTvdbId = request.HasTvdbId, - HasTmdbId = request.HasTmdbId, - HasOverview = request.HasOverview, - HasOfficialRating = request.HasOfficialRating, - HasParentalRating = request.HasParentalRating, - HasSpecialFeature = request.HasSpecialFeature, - HasSubtitles = request.HasSubtitles, - HasThemeSong = request.HasThemeSong, - HasThemeVideo = request.HasThemeVideo, - HasTrailer = request.HasTrailer, - Tags = request.GetTags(), - OfficialRatings = request.GetOfficialRatings(), - Genres = request.GetGenres(), - Studios = request.GetStudios(), - StudioIds = request.GetStudioIds(), - Person = request.Person, - PersonIds = request.GetPersonIds(), - PersonTypes = request.GetPersonTypes(), - Years = request.GetYears(), - ImageTypes = request.GetImageTypes().ToArray(), - VideoTypes = request.GetVideoTypes().ToArray(), - AdjacentTo = request.AdjacentTo - }; - - if (!string.IsNullOrWhiteSpace(request.Ids)) - { - query.CollapseBoxSetItems = false; - } - - foreach (var filter in request.GetFilters()) - { - switch (filter) - { - case ItemFilter.Dislikes: - query.IsLiked = false; - break; - case ItemFilter.IsFavorite: - query.IsFavorite = true; - break; - case ItemFilter.IsFavoriteOrLikes: - query.IsFavoriteOrLiked = true; - break; - case ItemFilter.IsFolder: - query.IsFolder = true; - break; - case ItemFilter.IsNotFolder: - query.IsFolder = false; - break; - case ItemFilter.IsPlayed: - query.IsPlayed = true; - break; - case ItemFilter.IsRecentlyAdded: - break; - case ItemFilter.IsResumable: - query.IsResumable = true; - break; - case ItemFilter.IsUnplayed: - query.IsPlayed = false; - break; - case ItemFilter.Likes: - query.IsLiked = true; - break; - } - } - - if (request.HasQueryLimit) - query.Limit = request.Limit; - return query; - } - - /// <summary> Applies filtering. </summary> - /// <param name="items"> The items. </param> - /// <param name="filter"> The filter. </param> - /// <param name="user"> The user. </param> - /// <param name="repository"> The repository. </param> - /// <returns> IEnumerable{BaseItem}. </returns> - internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user, IUserDataManager repository) - { - // Avoid implicitly captured closure - var currentUser = user; - - switch (filter) - { - case ItemFilter.IsFavoriteOrLikes: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - - if (userdata == null) - { - return false; - } - - var likes = userdata.Likes ?? false; - var favorite = userdata.IsFavorite; - - return likes || favorite; - }); - - case ItemFilter.Likes: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - - return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; - }); - - case ItemFilter.Dislikes: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - - return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; - }); - - case ItemFilter.IsFavorite: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - - return userdata != null && userdata.IsFavorite; - }); - - case ItemFilter.IsResumable: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - - return userdata != null && userdata.PlaybackPositionTicks > 0; - }); - - case ItemFilter.IsPlayed: - return items.Where(item => item.IsPlayed(currentUser)); - - case ItemFilter.IsUnplayed: - return items.Where(item => item.IsUnplayed(currentUser)); - - case ItemFilter.IsFolder: - return items.Where(item => item.IsFolder); - - case ItemFilter.IsNotFolder: - return items.Where(item => !item.IsFolder); - - case ItemFilter.IsRecentlyAdded: - return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10); - } - - return items; - } - - /// <summary> Applies the additional filters. </summary> - /// <param name="request"> The request. </param> - /// <param name="i"> Zero-based index of the. </param> - /// <param name="user"> The user. </param> - /// <param name="isPreFiltered"> true if this object is pre filtered. </param> - /// <param name="libraryManager"> Manager for library. </param> - /// <returns> true if it succeeds, false if it fails. </returns> + /// <summary> The reports service. </summary> + /// <seealso cref="T:MediaBrowser.Api.BaseApiService"/> + public class ReportsService : BaseApiService + { + #region [Constructors] + + /// <summary> + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class. </summary> + /// <param name="userManager"> Manager for user. </param> + /// <param name="libraryManager"> Manager for library. </param> + /// <param name="localization"> The localization. </param> + /// <param name="activityManager"> Manager for activity. </param> + public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IActivityManager activityManager, IActivityRepository repo) + { + _userManager = userManager; + _libraryManager = libraryManager; + _localization = localization; + _activityManager = activityManager; + _repo = repo; + } + + #endregion + + #region [Private Fields] + + private readonly IActivityManager _activityManager; ///< Manager for activity + + /// <summary> Manager for library. </summary> + private readonly ILibraryManager _libraryManager; ///< Manager for library + /// <summary> The localization. </summary> + + private readonly ILocalizationManager _localization; ///< The localization + + private readonly IActivityRepository _repo; + + /// <summary> Manager for user. </summary> + private readonly IUserManager _userManager; ///< Manager for user + + #endregion + + #region [Public Methods] + + /// <summary> Gets the given request. </summary> + /// <param name="request"> The request. </param> + /// <returns> A Task<object> </returns> + public async Task<object> Get(GetActivityLogs request) + { + ReportResult result = await GetReportActivities(request).ConfigureAwait(false); + return ToOptimizedResult(result); + } + + /// <summary> Gets the given request. </summary> + /// <param name="request"> The request. </param> + /// <returns> A Task<object> </returns> + public async Task<object> Get(GetReportHeaders request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; + + ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView); + ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + + List<ReportHeader> result = new List<ReportHeader>(); + switch (reportViewType) + { + case ReportViewType.ReportData: + ReportBuilder dataBuilder = new ReportBuilder(_libraryManager); + result = dataBuilder.GetHeaders(request); + break; + case ReportViewType.ReportStatistics: + break; + case ReportViewType.ReportActivities: + ReportActivitiesBuilder activityBuilder = new ReportActivitiesBuilder(_libraryManager, _userManager); + result = activityBuilder.GetHeaders(request); + break; + } + + return ToOptimizedResult(result); + + } + + /// <summary> Gets the given request. </summary> + /// <param name="request"> The request. </param> + /// <returns> A Task<object> </returns> + public async Task<object> Get(GetItemReport request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; + + var reportResult = await GetReportResult(request); + + return ToOptimizedResult(reportResult); + } + + /// <summary> Gets the given request. </summary> + /// <param name="request"> The request. </param> + /// <returns> A Task<object> </returns> + public async Task<object> Get(GetReportStatistics request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; + var reportResult = await GetReportStatistic(request); + + return ToOptimizedResult(reportResult); + } + + /// <summary> Gets the given request. </summary> + /// <param name="request"> The request. </param> + /// <returns> A Task<object> </returns> + public async Task<object> Get(GetReportDownload request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; + + ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView); + var headers = new Dictionary<string, string>(); + string fileExtension = "csv"; + string contentType = "text/plain;charset='utf-8'"; + + switch (request.ExportType) + { + case ReportExportType.CSV: + break; + case ReportExportType.Excel: + contentType = "application/vnd.ms-excel"; + fileExtension = "xls"; + break; + } + + var filename = "ReportExport." + fileExtension; + headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename); + headers["Content-Encoding"] = "UTF-8"; + + ReportResult result = null; + switch (reportViewType) + { + case ReportViewType.ReportStatistics: + case ReportViewType.ReportData: + ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + ReportBuilder dataBuilder = new ReportBuilder(_libraryManager); + QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false); + result = dataBuilder.GetResult(queryResult.Items, request); + result.TotalRecordCount = queryResult.TotalRecordCount; + break; + case ReportViewType.ReportActivities: + result = await GetReportActivities(request).ConfigureAwait(false); + break; + } + + string returnResult = string.Empty; + switch (request.ExportType) + { + case ReportExportType.CSV: + returnResult = new ReportExport().ExportToCsv(result); + break; + case ReportExportType.Excel: + returnResult = new ReportExport().ExportToExcel(result); + break; + } + + object ro = ResultFactory.GetResult(returnResult, contentType, headers); + return ro; + } + + #endregion + + #region [Internal Methods] + + /// <summary> Applies filtering. </summary> + /// <param name="items"> The items. </param> + /// <param name="filter"> The filter. </param> + /// <param name="user"> The user. </param> + /// <param name="repository"> The repository. </param> + /// <returns> IEnumerable{BaseItem}. </returns> + internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user, IUserDataManager repository) + { + // Avoid implicitly captured closure + var currentUser = user; + + switch (filter) + { + case ItemFilter.IsFavoriteOrLikes: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + if (userdata == null) + { + return false; + } + + var likes = userdata.Likes ?? false; + var favorite = userdata.IsFavorite; + + return likes || favorite; + }); + + case ItemFilter.Likes: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; + }); + + case ItemFilter.Dislikes: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; + }); + + case ItemFilter.IsFavorite: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.IsFavorite; + }); + + case ItemFilter.IsResumable: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.PlaybackPositionTicks > 0; + }); + + case ItemFilter.IsPlayed: + return items.Where(item => item.IsPlayed(currentUser)); + + case ItemFilter.IsUnplayed: + return items.Where(item => item.IsUnplayed(currentUser)); + + case ItemFilter.IsFolder: + return items.Where(item => item.IsFolder); + + case ItemFilter.IsNotFolder: + return items.Where(item => !item.IsFolder); + + case ItemFilter.IsRecentlyAdded: + return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10); + } + + return items; + } + + #endregion + + #region [Private Methods] + + /// <summary> Applies the additional filters. </summary> + /// <param name="request"> The request. </param> + /// <param name="i"> Zero-based index of the. </param> + /// <param name="user"> The user. </param> + /// <param name="isPreFiltered"> true if this object is pre filtered. </param> + /// <param name="libraryManager"> Manager for library. </param> + /// <returns> true if it succeeds, false if it fails. </returns> private bool ApplyAdditionalFilters(BaseReportRequest request, BaseItem i, User user, bool isPreFiltered, ILibraryManager libraryManager) - { - var video = i as Video; - - if (!isPreFiltered) - { - var mediaTypes = request.GetMediaTypes(); - if (mediaTypes.Length > 0) - { - if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - } - - if (request.IsPlayed.HasValue) - { - var val = request.IsPlayed.Value; - if (i.IsPlayed(user) != val) - { - return false; - } - } - - // Exclude item types - var excluteItemTypes = request.GetExcludeItemTypes(); - if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - // Include item types - var includeItemTypes = request.GetIncludeItemTypes(); - if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - if (request.IsInBoxSet.HasValue) - { - var val = request.IsInBoxSet.Value; - if (i.Parents.OfType<BoxSet>().Any() != val) - { - return false; - } - } - - // Filter by Video3DFormat - if (request.Is3D.HasValue) - { - var val = request.Is3D.Value; - - if (video == null || val != video.Video3DFormat.HasValue) - { - return false; - } - } - - if (request.IsHD.HasValue) - { - var val = request.IsHD.Value; - - if (video == null || val != video.IsHD) - { - return false; - } - } - - if (request.IsUnidentified.HasValue) - { - var val = request.IsUnidentified.Value; - if (i.IsUnidentified != val) - { - return false; - } - } - - if (request.IsLocked.HasValue) - { - var val = request.IsLocked.Value; - if (i.IsLocked != val) - { - return false; - } - } - - if (request.HasOverview.HasValue) - { - var filterValue = request.HasOverview.Value; - - var hasValue = !string.IsNullOrEmpty(i.Overview); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.HasImdbId.HasValue) - { - var filterValue = request.HasImdbId.Value; - - var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb)); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.HasTmdbId.HasValue) - { - var filterValue = request.HasTmdbId.Value; - - var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.HasTvdbId.HasValue) - { - var filterValue = request.HasTvdbId.Value; - - var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.IsYearMismatched.HasValue) - { - var filterValue = request.IsYearMismatched.Value; - - if (UserViewBuilder.IsYearMismatched(i, libraryManager) != filterValue) - { - return false; - } - } - - if (request.HasOfficialRating.HasValue) - { - var filterValue = request.HasOfficialRating.Value; - - var hasValue = !string.IsNullOrEmpty(i.OfficialRating); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.IsPlaceHolder.HasValue) - { - var filterValue = request.IsPlaceHolder.Value; - - var isPlaceHolder = false; - - var hasPlaceHolder = i as ISupportsPlaceHolders; - - if (hasPlaceHolder != null) - { - isPlaceHolder = hasPlaceHolder.IsPlaceHolder; - } - - if (isPlaceHolder != filterValue) - { - return false; - } - } - - if (request.HasSpecialFeature.HasValue) - { - var filterValue = request.HasSpecialFeature.Value; - - var movie = i as IHasSpecialFeatures; - - if (movie != null) - { - var ok = filterValue - ? movie.SpecialFeatureIds.Count > 0 - : movie.SpecialFeatureIds.Count == 0; - - if (!ok) - { - return false; - } - } - else - { - return false; - } - } - - if (request.HasSubtitles.HasValue) - { - var val = request.HasSubtitles.Value; - - if (video == null || val != video.HasSubtitles) - { - return false; - } - } - - if (request.HasParentalRating.HasValue) - { - var val = request.HasParentalRating.Value; - - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (val) - { - if (string.IsNullOrEmpty(rating)) - { - return false; - } - } - else - { - if (!string.IsNullOrEmpty(rating)) - { - return false; - } - } - } - - if (request.HasTrailer.HasValue) - { - var val = request.HasTrailer.Value; - var trailerCount = 0; - - var hasTrailers = i as IHasTrailers; - if (hasTrailers != null) - { - trailerCount = hasTrailers.GetTrailerIds().Count; - } - - var ok = val ? trailerCount > 0 : trailerCount == 0; - - if (!ok) - { - return false; - } - } - - if (request.HasThemeSong.HasValue) - { - var filterValue = request.HasThemeSong.Value; - - var themeCount = 0; - var iHasThemeMedia = i as IHasThemeMedia; - - if (iHasThemeMedia != null) - { - themeCount = iHasThemeMedia.ThemeSongIds.Count; - } - var ok = filterValue ? themeCount > 0 : themeCount == 0; - - if (!ok) - { - return false; - } - } - - if (request.HasThemeVideo.HasValue) - { - var filterValue = request.HasThemeVideo.Value; - - var themeCount = 0; - var iHasThemeMedia = i as IHasThemeMedia; - - if (iHasThemeMedia != null) - { - themeCount = iHasThemeMedia.ThemeVideoIds.Count; - } - var ok = filterValue ? themeCount > 0 : themeCount == 0; - - if (!ok) - { - return false; - } - } - - // Apply tag filter - var tags = request.GetTags(); - if (tags.Length > 0) - { - var hasTags = i as IHasTags; - if (hasTags == null) - { - return false; - } - if (!(tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - } - - // Apply official rating filter - var officialRatings = request.GetOfficialRatings(); - if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty)) - { - return false; - } - - // Apply genre filter - var genres = request.GetGenres(); - if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - - // Filter by VideoType - var videoTypes = request.GetVideoTypes(); - if (videoTypes.Length > 0 && (video == null || !videoTypes.Contains(video.VideoType))) - { - return false; - } - - var imageTypes = request.GetImageTypes().ToList(); - if (imageTypes.Count > 0) - { - if (!(imageTypes.Any(i.HasImage))) - { - return false; - } - } - - // Apply studio filter - var studios = request.GetStudios(); - if (studios.Length > 0 && !studios.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - - // Apply studio filter - var studioIds = request.GetStudioIds(); - if (studioIds.Length > 0 && !studioIds.Any(id => - { - var studioItem = libraryManager.GetItemById(id); - return studioItem != null && i.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase); - })) - { - return false; - } - - // Apply year filter - var years = request.GetYears(); - if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value))) - { - return false; - } - - // Apply person filter - var personIds = request.GetPersonIds(); - if (personIds.Length > 0) - { - var names = personIds - .Select(libraryManager.GetItemById) - .Select(p => p == null ? "-1" : p.Name) - .ToList(); - - if (!(names.Any(v => i.People.Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - } - - // Apply person filter - if (!string.IsNullOrEmpty(request.Person)) - { - var personTypes = request.GetPersonTypes(); - - if (personTypes.Length == 0) - { - if (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) - { - return false; - } - } - else - { - var types = personTypes; - - var ok = new[] { i }.Any(item => - item.People != null && - item.People.Any(p => - p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase)))); - - if (!ok) - { - return false; - } - } - } - } - - if (request.MinCommunityRating.HasValue) - { - var val = request.MinCommunityRating.Value; - - if (!(i.CommunityRating.HasValue && i.CommunityRating >= val)) - { - return false; - } - } - - if (request.MinCriticRating.HasValue) - { - var val = request.MinCriticRating.Value; - - var hasCriticRating = i as IHasCriticRating; - - if (hasCriticRating != null) - { - if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val)) - { - return false; - } - } - else - { - return false; - } - } - - // Artists - if (!string.IsNullOrEmpty(request.ArtistIds)) - { - var artistIds = request.ArtistIds.Split('|'); - - var audio = i as IHasArtist; - - if (!(audio != null && artistIds.Any(id => - { - var artistItem = libraryManager.GetItemById(id); - return artistItem != null && audio.HasAnyArtist(artistItem.Name); - }))) - { - return false; - } - } - - // Artists - if (!string.IsNullOrEmpty(request.Artists)) - { - var artists = request.Artists.Split('|'); - - var audio = i as IHasArtist; - - if (!(audio != null && artists.Any(audio.HasAnyArtist))) - { - return false; - } - } - - // Albums - if (!string.IsNullOrEmpty(request.Albums)) - { - var albums = request.Albums.Split('|'); - - var audio = i as Audio; - - if (audio != null) - { - if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - } - - var album = i as MusicAlbum; - - if (album != null) - { - if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - } - - var musicVideo = i as MusicVideo; - - if (musicVideo != null) - { - if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - } - - return false; - } - - // Min index number - if (request.MinIndexNumber.HasValue) - { - if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value)) - { - return false; - } - } - - // Min official rating - if (!string.IsNullOrEmpty(request.MinOfficialRating)) - { - var level = _localization.GetRatingLevel(request.MinOfficialRating); - - if (level.HasValue) - { - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (!string.IsNullOrEmpty(rating)) - { - var itemLevel = _localization.GetRatingLevel(rating); - - if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value)) - { - return false; - } - } - } - } - - // Max official rating - if (!string.IsNullOrEmpty(request.MaxOfficialRating)) - { - var level = _localization.GetRatingLevel(request.MaxOfficialRating); - - if (level.HasValue) - { - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (!string.IsNullOrEmpty(rating)) - { - var itemLevel = _localization.GetRatingLevel(rating); - - if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value)) - { - return false; - } - } - } - } - - // LocationTypes - if (!string.IsNullOrEmpty(request.LocationTypes)) - { - var vals = request.LocationTypes.Split(','); - if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) - { - var vals = request.ExcludeLocationTypes.Split(','); - if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater)) - { - var ok = new[] { i }.OfType<IHasAlbumArtist>() - .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1); - - if (!ok) - { - return false; - } - } - - // Filter by Series Status - if (!string.IsNullOrEmpty(request.SeriesStatus)) - { - var vals = request.SeriesStatus.Split(','); - - var ok = new[] { i }.OfType<Series>().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase)); - - if (!ok) - { - return false; - } - } - - // Filter by Series AirDays - if (!string.IsNullOrEmpty(request.AirDays)) - { - var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)); - - var ok = new[] { i }.OfType<Series>().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d))); - - if (!ok) - { - return false; - } - } - - if (request.MinPlayers.HasValue) - { - var filterValue = request.MinPlayers.Value; - - var game = i as Game; - - if (game != null) - { - var players = game.PlayersSupported ?? 1; - - var ok = players >= filterValue; - - if (!ok) - { - return false; - } - } - else - { - return false; - } - } - - if (request.MaxPlayers.HasValue) - { - var filterValue = request.MaxPlayers.Value; - - var game = i as Game; - - if (game != null) - { - var players = game.PlayersSupported ?? 1; - - var ok = players <= filterValue; - - if (!ok) - { - return false; - } - } - else - { - return false; - } - } - - if (request.ParentIndexNumber.HasValue) - { - var filterValue = request.ParentIndexNumber.Value; - - var episode = i as Episode; - - if (episode != null) - { - if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue) - { - return false; - } - } - - var song = i as Audio; - - if (song != null) - { - if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue) - { - return false; - } - } - } - - if (request.AiredDuringSeason.HasValue) - { - var episode = i as Episode; - - if (episode == null) - { - return false; - } - - if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any()) - { - return false; - } - } - - if (!string.IsNullOrEmpty(request.MinPremiereDate)) - { - var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - - if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(request.MaxPremiereDate)) - { - var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - - if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date)) - { - return false; - } - } - - return true; - } - - /// <summary> Applies the paging. </summary> - /// <param name="request"> The request. </param> - /// <param name="items"> The items. </param> - /// <returns> IEnumerable{BaseItem}. </returns> - private IEnumerable<BaseItem> ApplyPaging(GetItems request, IEnumerable<BaseItem> items) - { - // Start at - if (request.StartIndex.HasValue) - { - items = items.Skip(request.StartIndex.Value); - } - - // Return limit - if (request.Limit.HasValue) - { - items = items.Take(request.Limit.Value); - } - - return items; - } - - } + { + var video = i as Video; + + if (!isPreFiltered) + { + var mediaTypes = request.GetMediaTypes(); + if (mediaTypes.Length > 0) + { + if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) + { + return false; + } + } + + if (request.IsPlayed.HasValue) + { + var val = request.IsPlayed.Value; + if (i.IsPlayed(user) != val) + { + return false; + } + } + + // Exclude item types + var excluteItemTypes = request.GetExcludeItemTypes(); + if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + // Include item types + var includeItemTypes = request.GetIncludeItemTypes(); + if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + if (request.IsInBoxSet.HasValue) + { + var val = request.IsInBoxSet.Value; + if (i.Parents.OfType<BoxSet>().Any() != val) + { + return false; + } + } + + // Filter by Video3DFormat + if (request.Is3D.HasValue) + { + var val = request.Is3D.Value; + + if (video == null || val != video.Video3DFormat.HasValue) + { + return false; + } + } + + if (request.IsHD.HasValue) + { + var val = request.IsHD.Value; + + if (video == null || val != video.IsHD) + { + return false; + } + } + + if (request.IsUnidentified.HasValue) + { + var val = request.IsUnidentified.Value; + if (i.IsUnidentified != val) + { + return false; + } + } + + if (request.IsLocked.HasValue) + { + var val = request.IsLocked.Value; + if (i.IsLocked != val) + { + return false; + } + } + + if (request.HasOverview.HasValue) + { + var filterValue = request.HasOverview.Value; + + var hasValue = !string.IsNullOrEmpty(i.Overview); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.HasImdbId.HasValue) + { + var filterValue = request.HasImdbId.Value; + + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.HasTmdbId.HasValue) + { + var filterValue = request.HasTmdbId.Value; + + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.HasTvdbId.HasValue) + { + var filterValue = request.HasTvdbId.Value; + + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.IsYearMismatched.HasValue) + { + var filterValue = request.IsYearMismatched.Value; + + if (UserViewBuilder.IsYearMismatched(i, libraryManager) != filterValue) + { + return false; + } + } + + if (request.HasOfficialRating.HasValue) + { + var filterValue = request.HasOfficialRating.Value; + + var hasValue = !string.IsNullOrEmpty(i.OfficialRating); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.IsPlaceHolder.HasValue) + { + var filterValue = request.IsPlaceHolder.Value; + + var isPlaceHolder = false; + + var hasPlaceHolder = i as ISupportsPlaceHolders; + + if (hasPlaceHolder != null) + { + isPlaceHolder = hasPlaceHolder.IsPlaceHolder; + } + + if (isPlaceHolder != filterValue) + { + return false; + } + } + + if (request.HasSpecialFeature.HasValue) + { + var filterValue = request.HasSpecialFeature.Value; + + var movie = i as IHasSpecialFeatures; + + if (movie != null) + { + var ok = filterValue + ? movie.SpecialFeatureIds.Count > 0 + : movie.SpecialFeatureIds.Count == 0; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (request.HasSubtitles.HasValue) + { + var val = request.HasSubtitles.Value; + + if (video == null || val != video.HasSubtitles) + { + return false; + } + } + + if (request.HasParentalRating.HasValue) + { + var val = request.HasParentalRating.Value; + + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = i.OfficialRating; + } + + if (val) + { + if (string.IsNullOrEmpty(rating)) + { + return false; + } + } + else + { + if (!string.IsNullOrEmpty(rating)) + { + return false; + } + } + } + + if (request.HasTrailer.HasValue) + { + var val = request.HasTrailer.Value; + var trailerCount = 0; + + var hasTrailers = i as IHasTrailers; + if (hasTrailers != null) + { + trailerCount = hasTrailers.GetTrailerIds().Count; + } + + var ok = val ? trailerCount > 0 : trailerCount == 0; + + if (!ok) + { + return false; + } + } + + if (request.HasThemeSong.HasValue) + { + var filterValue = request.HasThemeSong.Value; + + var themeCount = 0; + var iHasThemeMedia = i as IHasThemeMedia; + + if (iHasThemeMedia != null) + { + themeCount = iHasThemeMedia.ThemeSongIds.Count; + } + var ok = filterValue ? themeCount > 0 : themeCount == 0; + + if (!ok) + { + return false; + } + } + + if (request.HasThemeVideo.HasValue) + { + var filterValue = request.HasThemeVideo.Value; + + var themeCount = 0; + var iHasThemeMedia = i as IHasThemeMedia; + + if (iHasThemeMedia != null) + { + themeCount = iHasThemeMedia.ThemeVideoIds.Count; + } + var ok = filterValue ? themeCount > 0 : themeCount == 0; + + if (!ok) + { + return false; + } + } + + // Apply tag filter + var tags = request.GetTags(); + if (tags.Length > 0) + { + var hasTags = i as IHasTags; + if (hasTags == null) + { + return false; + } + if (!(tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + } + + // Apply official rating filter + var officialRatings = request.GetOfficialRatings(); + if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty)) + { + return false; + } + + // Apply genre filter + var genres = request.GetGenres(); + if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + + // Filter by VideoType + var videoTypes = request.GetVideoTypes(); + if (videoTypes.Length > 0 && (video == null || !videoTypes.Contains(video.VideoType))) + { + return false; + } + + var imageTypes = request.GetImageTypes().ToList(); + if (imageTypes.Count > 0) + { + if (!(imageTypes.Any(i.HasImage))) + { + return false; + } + } + + // Apply studio filter + var studios = request.GetStudios(); + if (studios.Length > 0 && !studios.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) + { + return false; + } + + // Apply studio filter + var studioIds = request.GetStudioIds(); + if (studioIds.Length > 0 && !studioIds.Any(id => + { + var studioItem = libraryManager.GetItemById(id); + return studioItem != null && i.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase); + })) + { + return false; + } + + // Apply year filter + var years = request.GetYears(); + if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value))) + { + return false; + } + + // Apply person filter + var personIds = request.GetPersonIds(); + if (personIds.Length > 0) + { + var names = personIds + .Select(libraryManager.GetItemById) + .Select(p => p == null ? "-1" : p.Name) + .ToList(); + + if (!(names.Any(v => i.People.Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + } + + // Apply person filter + if (!string.IsNullOrEmpty(request.Person)) + { + var personTypes = request.GetPersonTypes(); + + if (personTypes.Length == 0) + { + if (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) + { + return false; + } + } + else + { + var types = personTypes; + + var ok = new[] { i }.Any(item => + item.People != null && + item.People.Any(p => + p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase)))); + + if (!ok) + { + return false; + } + } + } + } + + if (request.MinCommunityRating.HasValue) + { + var val = request.MinCommunityRating.Value; + + if (!(i.CommunityRating.HasValue && i.CommunityRating >= val)) + { + return false; + } + } + + if (request.MinCriticRating.HasValue) + { + var val = request.MinCriticRating.Value; + + var hasCriticRating = i as IHasCriticRating; + + if (hasCriticRating != null) + { + if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val)) + { + return false; + } + } + else + { + return false; + } + } + + // Artists + if (!string.IsNullOrEmpty(request.ArtistIds)) + { + var artistIds = request.ArtistIds.Split('|'); + + var audio = i as IHasArtist; + + if (!(audio != null && artistIds.Any(id => + { + var artistItem = libraryManager.GetItemById(id); + return artistItem != null && audio.HasAnyArtist(artistItem.Name); + }))) + { + return false; + } + } + + // Artists + if (!string.IsNullOrEmpty(request.Artists)) + { + var artists = request.Artists.Split('|'); + + var audio = i as IHasArtist; + + if (!(audio != null && artists.Any(audio.HasAnyArtist))) + { + return false; + } + } + + // Albums + if (!string.IsNullOrEmpty(request.Albums)) + { + var albums = request.Albums.Split('|'); + + var audio = i as Audio; + + if (audio != null) + { + if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + } + + var album = i as MusicAlbum; + + if (album != null) + { + if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + } + + var musicVideo = i as MusicVideo; + + if (musicVideo != null) + { + if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + } + + return false; + } + + // Min index number + if (request.MinIndexNumber.HasValue) + { + if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value)) + { + return false; + } + } + + // Min official rating + if (!string.IsNullOrEmpty(request.MinOfficialRating)) + { + var level = _localization.GetRatingLevel(request.MinOfficialRating); + + if (level.HasValue) + { + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = i.OfficialRating; + } + + if (!string.IsNullOrEmpty(rating)) + { + var itemLevel = _localization.GetRatingLevel(rating); + + if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value)) + { + return false; + } + } + } + } + + // Max official rating + if (!string.IsNullOrEmpty(request.MaxOfficialRating)) + { + var level = _localization.GetRatingLevel(request.MaxOfficialRating); + + if (level.HasValue) + { + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = i.OfficialRating; + } + + if (!string.IsNullOrEmpty(rating)) + { + var itemLevel = _localization.GetRatingLevel(rating); + + if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value)) + { + return false; + } + } + } + } + + // LocationTypes + if (!string.IsNullOrEmpty(request.LocationTypes)) + { + var vals = request.LocationTypes.Split(','); + if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + return false; + } + } + + // ExcludeLocationTypes + if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) + { + var vals = request.ExcludeLocationTypes.Split(','); + if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater)) + { + var ok = new[] { i }.OfType<IHasAlbumArtist>() + .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1); + + if (!ok) + { + return false; + } + } + + // Filter by Series Status + if (!string.IsNullOrEmpty(request.SeriesStatus)) + { + var vals = request.SeriesStatus.Split(','); + + var ok = new[] { i }.OfType<Series>().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase)); + + if (!ok) + { + return false; + } + } + + // Filter by Series AirDays + if (!string.IsNullOrEmpty(request.AirDays)) + { + var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)); + + var ok = new[] { i }.OfType<Series>().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d))); + + if (!ok) + { + return false; + } + } + + if (request.MinPlayers.HasValue) + { + var filterValue = request.MinPlayers.Value; + + var game = i as Game; + + if (game != null) + { + var players = game.PlayersSupported ?? 1; + + var ok = players >= filterValue; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (request.MaxPlayers.HasValue) + { + var filterValue = request.MaxPlayers.Value; + + var game = i as Game; + + if (game != null) + { + var players = game.PlayersSupported ?? 1; + + var ok = players <= filterValue; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (request.ParentIndexNumber.HasValue) + { + var filterValue = request.ParentIndexNumber.Value; + + var episode = i as Episode; + + if (episode != null) + { + if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue) + { + return false; + } + } + + var song = i as Audio; + + if (song != null) + { + if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue) + { + return false; + } + } + } + + if (request.AiredDuringSeason.HasValue) + { + var episode = i as Episode; + + if (episode == null) + { + return false; + } + + if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any()) + { + return false; + } + } + + if (!string.IsNullOrEmpty(request.MinPremiereDate)) + { + var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + + if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(request.MaxPremiereDate)) + { + var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + + if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date)) + { + return false; + } + } + + return true; + } + + /// <summary> Applies the paging. </summary> + /// <param name="request"> The request. </param> + /// <param name="items"> The items. </param> + /// <returns> IEnumerable{BaseItem}. </returns> + private IEnumerable<BaseItem> ApplyPaging(GetItems request, IEnumerable<BaseItem> items) + { + // Start at + if (request.StartIndex.HasValue) + { + items = items.Skip(request.StartIndex.Value); + } + + // Return limit + if (request.Limit.HasValue) + { + items = items.Take(request.Limit.Value); + } + + return items; + } + + /// <summary> Gets items query. </summary> + /// <param name="request"> The request. </param> + /// <param name="user"> The user. </param> + /// <returns> The items query. </returns> + private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user) + { + var query = new InternalItemsQuery + { + User = user, + IsPlayed = request.IsPlayed, + MediaTypes = request.GetMediaTypes(), + IncludeItemTypes = request.GetIncludeItemTypes(), + ExcludeItemTypes = request.GetExcludeItemTypes(), + Recursive = true, + SortBy = request.GetOrderBy(), + SortOrder = request.SortOrder ?? SortOrder.Ascending, + + Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager), + StartIndex = request.StartIndex, + IsMissing = request.IsMissing, + IsVirtualUnaired = request.IsVirtualUnaired, + IsUnaired = request.IsUnaired, + CollapseBoxSetItems = request.CollapseBoxSetItems, + NameLessThan = request.NameLessThan, + NameStartsWith = request.NameStartsWith, + NameStartsWithOrGreater = request.NameStartsWithOrGreater, + HasImdbId = request.HasImdbId, + IsYearMismatched = request.IsYearMismatched, + IsUnidentified = request.IsUnidentified, + IsPlaceHolder = request.IsPlaceHolder, + IsLocked = request.IsLocked, + IsInBoxSet = request.IsInBoxSet, + IsHD = request.IsHD, + Is3D = request.Is3D, + HasTvdbId = request.HasTvdbId, + HasTmdbId = request.HasTmdbId, + HasOverview = request.HasOverview, + HasOfficialRating = request.HasOfficialRating, + HasParentalRating = request.HasParentalRating, + HasSpecialFeature = request.HasSpecialFeature, + HasSubtitles = request.HasSubtitles, + HasThemeSong = request.HasThemeSong, + HasThemeVideo = request.HasThemeVideo, + HasTrailer = request.HasTrailer, + Tags = request.GetTags(), + OfficialRatings = request.GetOfficialRatings(), + Genres = request.GetGenres(), + Studios = request.GetStudios(), + StudioIds = request.GetStudioIds(), + Person = request.Person, + PersonIds = request.GetPersonIds(), + PersonTypes = request.GetPersonTypes(), + Years = request.GetYears(), + ImageTypes = request.GetImageTypes().ToArray(), + VideoTypes = request.GetVideoTypes().ToArray(), + AdjacentTo = request.AdjacentTo + }; + + if (!string.IsNullOrWhiteSpace(request.Ids)) + { + query.CollapseBoxSetItems = false; + } + + foreach (var filter in request.GetFilters()) + { + switch (filter) + { + case ItemFilter.Dislikes: + query.IsLiked = false; + break; + case ItemFilter.IsFavorite: + query.IsFavorite = true; + break; + case ItemFilter.IsFavoriteOrLikes: + query.IsFavoriteOrLiked = true; + break; + case ItemFilter.IsFolder: + query.IsFolder = true; + break; + case ItemFilter.IsNotFolder: + query.IsFolder = false; + break; + case ItemFilter.IsPlayed: + query.IsPlayed = true; + break; + case ItemFilter.IsRecentlyAdded: + break; + case ItemFilter.IsResumable: + query.IsResumable = true; + break; + case ItemFilter.IsUnplayed: + query.IsPlayed = false; + break; + case ItemFilter.Likes: + query.IsLiked = true; + break; + } + } + + if (request.HasQueryLimit) + query.Limit = request.Limit; + return query; + } + + /// <summary> Gets query result. </summary> + /// <param name="request"> The request. </param> + /// <returns> The query result. </returns> + private async Task<QueryResult<BaseItem>> GetQueryResult(BaseReportRequest request) + { + // Placeholder in case needed later + request.Recursive = true; + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts"; + + var parentItem = string.IsNullOrEmpty(request.ParentId) ? + (user == null ? _libraryManager.RootFolder : user.RootFolder) : + _libraryManager.GetItemById(request.ParentId); + + var item = string.IsNullOrEmpty(request.ParentId) ? + user == null ? _libraryManager.RootFolder : user.RootFolder : + parentItem; + + IEnumerable<BaseItem> items; + + if (request.Recursive) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); + return result; + } + else + { + if (user == null) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); + return result; + } + + var userRoot = item as UserRootFolder; + + if (userRoot == null) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); + + return result; + } + + items = ((Folder)item).GetChildren(user, true); + } + + return new QueryResult<BaseItem> { Items = items.ToArray() }; + + } + + /// <summary> Gets report activities. </summary> + /// <param name="request"> The request. </param> + /// <returns> The report activities. </returns> + private Task<ReportResult> GetReportActivities(IReportsDownload request) + { + return Task<ReportResult>.Run(() => + { + DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? + (DateTime?)null : + DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + var queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); + //var queryResult = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); + + ReportActivitiesBuilder builder = new ReportActivitiesBuilder(_libraryManager, _userManager); + var result = builder.GetResult(queryResult, request); + result.TotalRecordCount = queryResult.TotalRecordCount; + return result; + + }); + + } + + /// <summary> Gets report result. </summary> + /// <param name="request"> The request. </param> + /// <returns> The report result. </returns> + private async Task<ReportResult> GetReportResult(GetItemReport request) + { + ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); + QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false); + ReportResult reportResult = reportBuilder.GetResult(queryResult.Items, request); + reportResult.TotalRecordCount = queryResult.TotalRecordCount; + + return reportResult; + } + + /// <summary> Gets report statistic. </summary> + /// <param name="request"> The request. </param> + /// <returns> The report statistic. </returns> + private async Task<ReportStatResult> GetReportStatistic(GetReportStatistics request) + { + ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false); + + ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager); + ReportStatResult reportResult = reportBuilder.GetResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5); + reportResult.TotalRecordCount = reportResult.Groups.Count(); + return reportResult; + } + + #endregion + + } } diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs index e297a2a57..e2169c9c3 100644 --- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs +++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs @@ -9,206 +9,265 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { - /// <summary> A report stat builder. </summary> - /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/> - public class ReportStatBuilder : ReportBuilderBase - { - /// <summary> - /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. </summary> - /// <param name="libraryManager"> Manager for library. </param> - public ReportStatBuilder(ILibraryManager libraryManager) - : base(libraryManager) - { - } - - /// <summary> Gets report stat result. </summary> - /// <param name="items"> The items. </param> - /// <param name="reportRowType"> Type of the report row. </param> - /// <param name="topItem"> The top item. </param> - /// <returns> The report stat result. </returns> - public ReportStatResult GetReportStatResult(BaseItem[] items, ReportViewType reportRowType, int topItem = 5) - { - ReportStatResult result = new ReportStatResult(); - result = this.GetResultGenres(result, items, topItem); - result = this.GetResultStudios(result, items, topItem); - result = this.GetResultPersons(result, items, topItem); - result = this.GetResultProductionYears(result, items, topItem); - result = this.GetResulProductionLocations(result, items, topItem); - result = this.GetResultCommunityRatings(result, items, topItem); - result = this.GetResultParentalRatings(result, items, topItem); - - switch (reportRowType) - { - case ReportViewType.Season: - case ReportViewType.Series: - case ReportViewType.MusicAlbum: - case ReportViewType.MusicArtist: - case ReportViewType.Game: - break; - case ReportViewType.Movie: - case ReportViewType.BoxSet: - - break; - case ReportViewType.Book: - case ReportViewType.Episode: - case ReportViewType.Video: - case ReportViewType.MusicVideo: - case ReportViewType.Trailer: - case ReportViewType.Audio: - case ReportViewType.BaseItem: - default: - break; - } - - result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList(); - - return result; - } - - private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderGenres"), topItem, - items.SelectMany(x => x.Genres) - .GroupBy(x => x) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key, - Value = x.Count().ToString(), - Id = GetGenreID(x.Key) - })); - return result; - - } - - private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderStudios"), topItem, - items.SelectMany(x => x.Studios) - .GroupBy(x => x) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key, - Value = x.Count().ToString(), - Id = GetStudioID(x.Key) - }) - ); - - return result; - - } - - private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - List<string> t = new List<string> { PersonType.Actor, PersonType.Composer, PersonType.Director, PersonType.GuestStar, PersonType.Producer, PersonType.Writer, "Artist", "AlbumArtist" }; - foreach (var item in t) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem, - items.SelectMany(x => x.People) - .Where(n => n.Type == item) - .GroupBy(x => x.Name) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key, - Value = x.Count().ToString(), - Id = GetPersonID(x.Key) - }) - ); - } - - return result; - } - - private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("LabelCommunityRating"), topItem, - items.Where(x => x.CommunityRating != null && x.CommunityRating > 0) - .GroupBy(x => x.CommunityRating) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); - - return result; - } - - private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderParentalRatings"), topItem, - items.Where(x => x.OfficialRating != null) - .GroupBy(x => x.OfficialRating) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); - - return result; - } - - - private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderYears"), topItem, - items.Where(x => x.ProductionYear != null && x.ProductionYear > 0) - .GroupBy(x => x.ProductionYear) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); - - return result; - } - - private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderCountries"), topItem, - items.OfType<IHasProductionLocations>() - .Where(x => x.ProductionLocations != null) - .SelectMany(x => x.ProductionLocations) - .GroupBy(x => x) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); - - return result; - } - - - /// <summary> Gets the groups. </summary> - /// <param name="result"> The result. </param> - /// <param name="header"> The header. </param> - /// <param name="topItem"> The top item. </param> - /// <param name="top"> The top. </param> - private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable<ReportStatItem> top) - { - if (top.Count() > 0) - { - var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) }; - group.Items.AddRange(top); - result.Groups.Add(group); - } - } - } + /// <summary> A report stat builder. </summary> + /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/> + public class ReportStatBuilder : ReportBuilderBase + { + #region [Constructors] + + /// <summary> + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. </summary> + /// <param name="libraryManager"> Manager for library. </param> + public ReportStatBuilder(ILibraryManager libraryManager) + : base(libraryManager) + { + } + + #endregion + + #region [Public Methods] + + /// <summary> Gets report stat result. </summary> + /// <param name="items"> The items. </param> + /// <param name="reportIncludeItemTypes"> List of types of the report include items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The report stat result. </returns> + public ReportStatResult GetResult(BaseItem[] items, ReportIncludeItemTypes reportIncludeItemTypes, int topItem = 5) + { + ReportStatResult result = new ReportStatResult(); + result = this.GetResultGenres(result, items, topItem); + result = this.GetResultStudios(result, items, topItem); + result = this.GetResultPersons(result, items, topItem); + result = this.GetResultProductionYears(result, items, topItem); + result = this.GetResulProductionLocations(result, items, topItem); + result = this.GetResultCommunityRatings(result, items, topItem); + result = this.GetResultParentalRatings(result, items, topItem); + + switch (reportIncludeItemTypes) + { + case ReportIncludeItemTypes.Season: + case ReportIncludeItemTypes.Series: + case ReportIncludeItemTypes.MusicAlbum: + case ReportIncludeItemTypes.MusicArtist: + case ReportIncludeItemTypes.Game: + break; + case ReportIncludeItemTypes.Movie: + case ReportIncludeItemTypes.BoxSet: + + break; + case ReportIncludeItemTypes.Book: + case ReportIncludeItemTypes.Episode: + case ReportIncludeItemTypes.Video: + case ReportIncludeItemTypes.MusicVideo: + case ReportIncludeItemTypes.Trailer: + case ReportIncludeItemTypes.Audio: + case ReportIncludeItemTypes.BaseItem: + default: + break; + } + + result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList(); + + return result; + } + + #endregion + + #region [Protected Internal Methods] + /// <summary> Gets the headers. </summary> + /// <typeparam name="H"> Type of the header. </typeparam> + /// <param name="request"> The request. </param> + /// <returns> The headers. </returns> + /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/> + protected internal override List<ReportHeader> GetHeaders<H>(H request) + { + throw new NotImplementedException(); + } + + #endregion + + #region [Private Methods] + + /// <summary> Gets the groups. </summary> + /// <param name="result"> The result. </param> + /// <param name="header"> The header. </param> + /// <param name="topItem"> The top item. </param> + /// <param name="top"> The top. </param> + private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable<ReportStatItem> top) + { + if (top.Count() > 0) + { + var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) }; + group.Items.AddRange(top); + result.Groups.Add(group); + } + } + + /// <summary> Gets resul production locations. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The resul production locations. </returns> + private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderCountries"), topItem, + items.OfType<IHasProductionLocations>() + .Where(x => x.ProductionLocations != null) + .SelectMany(x => x.ProductionLocations) + .GroupBy(x => x) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); + + return result; + } + + /// <summary> Gets result community ratings. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The result community ratings. </returns> + private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("LabelCommunityRating"), topItem, + items.Where(x => x.CommunityRating != null && x.CommunityRating > 0) + .GroupBy(x => x.CommunityRating) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); + + return result; + } + + /// <summary> Gets result genres. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The result genres. </returns> + private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderGenres"), topItem, + items.SelectMany(x => x.Genres) + .GroupBy(x => x) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key, + Value = x.Count().ToString(), + Id = GetGenreID(x.Key) + })); + return result; + + } + + /// <summary> Gets result parental ratings. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The result parental ratings. </returns> + private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderParentalRatings"), topItem, + items.Where(x => x.OfficialRating != null) + .GroupBy(x => x.OfficialRating) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); + + return result; + } + + /// <summary> Gets result persons. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The result persons. </returns> + private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + List<string> t = new List<string> { PersonType.Actor, PersonType.Composer, PersonType.Director, PersonType.GuestStar, PersonType.Producer, PersonType.Writer, "Artist", "AlbumArtist" }; + foreach (var item in t) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem, + items.SelectMany(x => x.People) + .Where(n => n.Type == item) + .GroupBy(x => x.Name) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key, + Value = x.Count().ToString(), + Id = GetPersonID(x.Key) + }) + ); + } + + return result; + } + + /// <summary> Gets result production years. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The result production years. </returns> + private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderYears"), topItem, + items.Where(x => x.ProductionYear != null && x.ProductionYear > 0) + .GroupBy(x => x.ProductionYear) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); + + return result; + } + + /// <summary> Gets result studios. </summary> + /// <param name="result"> The result. </param> + /// <param name="items"> The items. </param> + /// <param name="topItem"> The top item. </param> + /// <returns> The result studios. </returns> + private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderStudios"), topItem, + items.SelectMany(x => x.Studios) + .GroupBy(x => x) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key, + Value = x.Count().ToString(), + Id = GetStudioID(x.Key) + }) + ); + + return result; + + } + + #endregion + + } } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 49a912012..3f28e90ea 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1448,6 +1448,7 @@ "OptionReportList": "List View", "OptionReportStatistics": "Statistics", "OptionReportGrouping": "Grouping", + "OptionReportActivities": "Activities Log", "HeaderExport": "Export", "HeaderColumns": "Columns", "ButtonReset": "Reset", @@ -1455,5 +1456,9 @@ "ButtonUnlockGuide": "Unlock Guide", "LabelEnableFullScreen": "Enable fullscreen mode", "LabelEnableChromecastAc3Passthrough": "Enable Chromecast AC3 Passthrough", - "LabelSyncPath": "Synced content path:" + "LabelSyncPath": "Synced content path:", + "HeaderOverview": "Overview", + "HeaderShortOverview": "Short Overview", + "HeaderType": "Type", + "HeaderSeverity": "Severity" } |
