using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Xml.Linq; namespace NzbDrone.Core.MetadataSource.Goodreads { /// /// This class models a work as defined by the Goodreads API. /// A work is the root concept of something written. Each book /// is a published edition of a piece of work. Most work properties /// are aggregate information over all the editions of a work. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public sealed class WorkResource : GoodreadsResource { public override string ElementName => "work"; /// /// The Goodreads Id for this work. /// public long Id { get; private set; } /// /// The number of books for this work. /// public int BooksCount { get; private set; } /// /// The Goodreads Book Id that is considered the best version of this work. /// Might not be populated. See the property for details, if provided. /// public long? BestBookId { get; private set; } /// /// The details for the best book of this work. Only populated /// if Goodreads provides it as part of the response. /// public BestBookResource BestBook { get; private set; } public long SeriesLinkId { get; private set; } /// /// If included in a list, this defines this work's position. /// public string UserPosition { get; private set; } /// /// The number of reviews of this work. /// public int ReviewsCount { get; private set; } /// /// The average rating of this work. /// public decimal AverageRating { get; private set; } /// /// The number of ratings of this work. /// public int RatingsCount { get; private set; } /// /// The number of text reviews of this work. /// public int TextReviewsCount { get; private set; } /// /// The original publication date of this work. /// public DateTime? OriginalPublicationDate { get; private set; } /// /// The original title of this work. /// public string OriginalTitle { get; private set; } /// /// The original language of this work. /// public int? OriginalLanguageId { get; private set; } /// /// The type of media for this work. /// public string MediaType { get; private set; } /// /// The distribution of all the ratings for this work. /// A dictionary of star rating -> number of ratings. /// public IReadOnlyDictionary RatingDistribution { get; private set; } public override void Parse(XElement element) { if (element.AncestorsAndSelf().First().Name != "work") { element = element.Element("work"); } Id = element.ElementAsLong("id"); var bestBookElement = element.Element("best_book"); if (bestBookElement != null) { BestBook = new BestBookResource(); BestBook.Parse(bestBookElement); } BestBookId = element.ElementAsNullableLong("best_book_id"); BooksCount = element.ElementAsInt("books_count"); ReviewsCount = element.ElementAsInt("reviews_count"); RatingsCount = element.ElementAsInt("ratings_count"); var average = element.ElementAsDecimal("average_rating"); if (average == 0 && RatingsCount > 0) { average = element.ElementAsDecimal("ratings_sum") / RatingsCount; } AverageRating = average; TextReviewsCount = element.ElementAsInt("text_reviews_count"); OriginalPublicationDate = element.ElementAsMultiDateField("original_publication"); OriginalTitle = element.ElementAsString("original_title"); OriginalLanguageId = element.ElementAsNullableInt("original_language_id"); MediaType = element.ElementAsString("media_type"); // Parse out the rating distribution var ratingDistributionElement = element.ElementAsString("rating_dist"); if (ratingDistributionElement != null) { var parts = ratingDistributionElement.Split('|'); if (parts.Length > 0) { var ratingDistribution = new Dictionary(); var ratings = parts.Select(x => x.Split(':')) .Where(x => x[0] != "total") .OrderBy(x => x[0]); foreach (var rating in ratings) { int.TryParse(rating[0], out var star); int.TryParse(rating[1], out var count); ratingDistribution.Add(star, count); } RatingDistribution = ratingDistribution; } } } internal void SetSeriesInfo(XElement element) { SeriesLinkId = element.ElementAsLong("id"); UserPosition = element.ElementAsString("user_position"); } } }