From b7cc41f142aecc890737068f4bfa4c809daaea10 Mon Sep 17 00:00:00 2001 From: Ethan Henderson Date: Sun, 25 Jul 2021 03:23:02 +0100 Subject: [PATCH] Delete files accidently left by merge --- analytix/youtube/analytics.py | 199 ------ analytix/youtube/reports.py | 1145 --------------------------------- docs/changelog.rst | 93 --- 3 files changed, 1437 deletions(-) delete mode 100644 analytix/youtube/analytics.py delete mode 100644 analytix/youtube/reports.py delete mode 100644 docs/changelog.rst diff --git a/analytix/youtube/analytics.py b/analytix/youtube/analytics.py deleted file mode 100644 index 86be2ac..0000000 --- a/analytix/youtube/analytics.py +++ /dev/null @@ -1,199 +0,0 @@ -import datetime as dt -import json - -import pandas as pd - -from analytix import InvalidRequest, NoAuthorisedService -from analytix.youtube import features, reports - - -class YouTubeAnalyticsReport: - """A class that provides an interface for working with data retrieved from the YouTube Analytics API. - - Args: - data (dict): The response data from the YouTube Analytics API. - type_ (ReportType): The type of report generated. - - Attributes: - data (dict): The response data from the YouTube Analytics API. - ncolumns (int): The number of columns in the report. - nrows (int): The number of rows in the report. - type (ReportType): The type of report generated. - """ - - __slots__ = ("data", "ncolumns", "nrows", "type") - - def __init__(self, data, type_): - self.data = data - self.ncolumns = len(data["columnHeaders"]) - self.nrows = len(data["rows"]) - self.type = type_ - - def to_dataframe(self): - """Returns the report as a Pandas DataFrame. - - Returns: - pandas.DataFrame: The DataFrame object. - """ - df = pd.DataFrame() - df = df.append(self.data["rows"]) - df.columns = [c["name"] for c in self.data["columnHeaders"]] - return df - - def to_json(self, file, indent=4): - """Exports the report to a JSON file in the same format as it was provided by the YouTube Analytics API. - - Args: - file (str | os.PathLike): The path in which the report should be saved. - indent (int): The number of spaces to use for indentation. Defaults to 4. - """ - if not file.endswith(".json"): - file += ".json" - with open(file, "w", encoding="utf-8") as f: - json.dump(self.data, f, indent=indent, ensure_ascii=False) - - def to_csv(self, file): - """Exports the report to a CSV file. - - Args: - file (str | os.PathLike): The path in which the report should be saved. - """ - if not file.endswith(".csv"): - file += ".csv" - self.to_dataframe().to_csv(file) - - def __str__(self): - return self.type - - -class YouTubeAnalytics: - """A class to retrieve data from the YouTube Analytics API. - - Args: - service (YouTubeService): The YouTube service to perform the operation on. - - Attributes: - service (YouTubeService): The YouTube service to perform the operation on. - """ - - __slots__ = ("service",) - - def __init__(self, service): - self.service = service - - def get_report_type(self, metrics=(), dimensions=(), filters={}, *, verify=True): - """Gets the report type that best matches the metrics, dimensions, and filters given. If :code:`verify` is False, this will return a :code:`Generic` report type. - - Args: - metrics (tuple[str, ...] | list[str]): The metrics (or columns) to retrieve. Defaults to an empty tuple. - dimensions (tuple[str, ...] | list[str]): The dimensions in which data is split. Defaults to an empty tuple. - filters (dict[str, str]): The filters to be applied when retrieving data. Defaults to an empty dictionary. - verify (bool): Whether to attempt to determine the report type dynamically. Defaults to True. - - Returns: - ReportType: The selected report type. - """ - if not verify: - return reports.Generic() - return reports.determine(metrics, dimensions, filters)() - - def get_verified_report_type(self, metrics=(), dimensions=(), filters={}): - """Like :code:`get_report_type`, but only returns a ReportType object if verification succeeds. - - Args: - metrics (tuple[str, ...] | list[str]): The metrics (or columns) to retrieve. Defaults to an empty tuple. - dimensions (tuple[str, ...] | list[str]): The dimensions in which data is split. Defaults to an empty tuple. - filters (dict[str, str]): The filters to be applied when retrieving data. Defaults to an empty dictionary. - - Returns: - ReportType | InvalidRequest: The selected report type or the error that caused verification to fail. - """ - r = self.get_report_type(metrics, dimensions, filters) - try: - r.verify(metrics, dimensions, filters, 5, ("views",)) - return r - except InvalidRequest as exc: - return exc - - def retrieve(self, metrics="all", verify=True, **kwargs): - """Executes an API request to pull down analytical information. - - .. warning:: - - The :code:`start_date` and :code:`end_date` parameters do not account for delayed analytics such as revenue. - - .. note:: - - This will retrieve video reports by default. To retrieve playlist reports, include '"isCurated": "1"' in your filters. - - Args: - metrics (tuple[str, ...] | list[str]): The metrics (or columns) to retrieve. Defaults to "all". - verify (bool): Whether to verify the requests before passing them to the API. Defaults to True. - start_date (datetime.date): The earliest date to fetch data for. Defaults to 28 days before the current date. - end_date (datetime.date): The latest date to fetch data for. Defaults to the current date. - currency (str): The currency to use for monetary analytics. This should be passed in ISO 4217 format. Defaults to USD. - dimensions (tuple[str, ...] | list[str]): The dimensions in which data is split. Defaults to an empty tuple. - filters (dict[str, str]): The filters to be applied when retrieving data. Defaults to an empty dictionary. - include_historical_data (bool): Whether data before the point in which the specified channel(s) were linked to the content owner should be retrieved. Defaults to False. - max_results (int | None): The maximum number of rows to fetch. Defaults to None. - sort_by (tuple[str, ...] | list[str]): The dimensions in which to sort the data. Defaults to an empty tuple. - start_index (int): The index of the first row to retrieve. Note that this is one-indexed. Defaults to 1. - - Returns: - YouTubeAnalyticsReport: The report object containing the data fetched from the YouTube Analytics API. - - Raises: - NoAuthorisedService: The given service has not been authorised. - InvalidRequest: The request was invalid. - """ - if not self.service.active: - raise NoAuthorisedService("the YouTube service has not been authorised") - - start_date = kwargs.get("start_date", dt.date.today() - dt.timedelta(days=28)) - end_date = kwargs.get("end_date", dt.date.today()) - currency = kwargs.get("currency", "USD") - dimensions = kwargs.get("dimensions", ()) - filters = kwargs.get("filters", {}) - include_historical_data = kwargs.get("include_historical_data", False) - max_results = kwargs.get("max_results", None) - sort_by = kwargs.get("sort_by", ()) - start_index = kwargs.get("start_index", 1) - - if not isinstance(dimensions, (tuple, list, set)): - raise InvalidRequest(f"expected tuple, list, or set of dimensions, got {type(dimensions).__name__}") - - if not isinstance(filters, dict): - raise InvalidRequest(f"expected dict of filters, got {type(filters).__name__}") - - if not isinstance(sort_by, (tuple, list, set)): - raise InvalidRequest(f"expected tuple, list, or set of sorting metrics, got {type(sort_by).__name__}") - - r = self.get_report_type(metrics, dimensions, filters, verify=verify) - r.verify(metrics, dimensions, filters, max_results, sort_by) - - if metrics == "all": - if not verify: - raise InvalidRequest("you must manually specify a list of metrics when not verifying") - metrics = r.metrics - - if filters: - filters = tuple(f"{k}=={v}" for k, v in filters.items()) - - return YouTubeAnalyticsReport( - self.service.active.reports() - .query( - ids="channel==MINE", - metrics=",".join(metrics), - startDate=start_date, - endDate=end_date, - currency=currency, - dimensions=",".join(dimensions), - filters=";".join(filters), - includeHistoricalChannelData=include_historical_data, - maxResults=max_results, - sort=",".join(sort_by), - startIndex=start_index, - ) - .execute(), - r, - ) diff --git a/analytix/youtube/reports.py b/analytix/youtube/reports.py deleted file mode 100644 index 90b3352..0000000 --- a/analytix/youtube/reports.py +++ /dev/null @@ -1,1145 +0,0 @@ -from analytix import InvalidRequest -from analytix.youtube import features -from analytix.youtube.features import FeatureAmount - - -class ReportType: - __slots__ = ("dimensions", "metrics", "filters") - - def __init__(self): - raise NotImplementedError("you should not use this class directly, nor call its __init__ method using super()") - - @property - def all_dimensions(self): - e = [] - for x in self.dimensions: - e.extend(x[1]) - return e - - @property - def all_filters(self): - e = [] - for x in self.filters: - e.extend(x[1]) - return e - - def verify(self, metrics, dimensions, filters, *args, **kwargs): - dimensions = set(dimensions) - filters = set(filters) - - if metrics != "all": - diff = set(metrics) - set(self.metrics) - if diff: - raise InvalidRequest("unexpected metric(s): " + ", ".join(diff)) - - diff = dimensions - set(self.all_dimensions) - if diff: - raise InvalidRequest("unexpected dimension(s): " + ", ".join(diff)) - - diff = filters - set(self.all_filters) - if diff: - raise InvalidRequest("unexpected filter(s): " + ", ".join(diff)) - - for amount, values in self.dimensions: - similarities = len(dimensions & values) - if amount == FeatureAmount.REQUIRED and similarities != len(values): - raise InvalidRequest(f"expected all dimensions from '{', '.join(values)}'") - elif amount == FeatureAmount.ZERO_OR_ONE and similarities > 1: - raise InvalidRequest(f"expected 0 or 1 dimensions from '{', '.join(values)}', got {len(dimensions)}") - elif amount == FeatureAmount.EXACTLY_ONE and similarities != 1: - raise InvalidRequest(f"expected 1 dimension from '{', '.join(values)}', got {len(dimensions)}") - elif amount == FeatureAmount.NON_ZERO and similarities == 0: - raise InvalidRequest( - f"expected at least 1 dimension from '{', '.join(values)}', got {len(dimensions)}" - ) - - for amount, values in self.filters: - similarities = len(filters & values) - if amount == FeatureAmount.REQUIRED and similarities != len(values): - raise InvalidRequest(f"expected all filters from '{', '.join(values)}'") - elif amount == FeatureAmount.ZERO_OR_ONE and similarities > 1: - raise InvalidRequest(f"expected 0 or 1 filters from '{', '.join(values)}', got {len(filters)}") - elif amount == FeatureAmount.EXACTLY_ONE and similarities != 1: - raise InvalidRequest(f"expected 1 filter from '{', '.join(values)}', got {len(filters)}") - elif amount == FeatureAmount.NON_ZERO and similarities == 0: - raise InvalidRequest(f"expected at least 1 filter from '{', '.join(values)}', got {len(filters)}") - - -class Generic(ReportType): - def __init__(self): - self.dimensions = [] - self.metrics = [] - self.filters = [] - - def verify(self, *args): - pass - - def __str__(self): - return "Generic" - - -class BasicUserActivity(ReportType): - def __init__(self): - self.dimensions = [] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - ] - - def __str__(self): - return "Basic user activity" - - -class BasicUserActivityUS(ReportType): - def __init__(self): - self.dimensions = [] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PROVINCE_METRICS - self.filters = [(FeatureAmount.REQUIRED, {"province"}), (FeatureAmount.ZERO_OR_ONE, {"video", "group"})] - - def __str__(self): - return "Basic user activity (US)" - - -class TimeBasedActivity(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.EXACTLY_ONE, {"day", "month"})] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - ] - - def __str__(self): - return "Time-based activity" - - -class TimeBasedActivityUS(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.EXACTLY_ONE, {"day", "month"})] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PROVINCE_METRICS - self.filters = [(FeatureAmount.REQUIRED, {"province"}), (FeatureAmount.ZERO_OR_ONE, {"video", "group"})] - - def __str__(self): - return "Time-based activity (US)" - - -class GeographyBasedActivity(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"country"})] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - ] - - def __str__(self): - return "Geography-based activity" - - -class GeographyBasedActivityUS(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"province"})] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PROVINCE_METRICS - self.filters = [ - (FeatureAmount.REQUIRED, {"country"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - ] - - def verify(self, metrics, dimensions, filters): - super().verify(metrics, dimensions, filters) - - if filters["country"] != "US": - raise InvalidRequest("the 'country' filter must be set to 'US'") - - def __str__(self): - return "Geography-based activity (US)" - - -class PlaybackDetailsSubscribedStatus(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.ZERO_OR_ONE, {"subscribedStatus"}), - (FeatureAmount.ZERO_OR_ONE, {"day", "month"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_SUBSCRIPTION_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ZERO_OR_ONE, {"subscribedStatus"}), - ] - - def __str__(self): - return "User activity by subscribed status" - - -class PlaybackDetailsSubscribedStatusUS(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.ZERO_OR_ONE, {"subscribedStatus"}), - (FeatureAmount.ZERO_OR_ONE, {"day", "month"}), - ] - self.metrics = ( - "views", - "redViews", - "estimatedMinutesWatched", - "estimatedRedMinutesWatched", - "averageViewDuration", - "averageViewPercentage", - "annotationClickThroughRate", - "annotationCloseRate", - "annotationImpressions", - "annotationClickableImpressions", - "annotationClosableImpressions", - "annotationClicks", - "annotationCloses", - "cardClickRate", - "cardTeaserClickRate", - "cardImpressions", - "cardTeaserImpressions", - "cardClicks", - "cardTeaserClicks", - ) - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"province", "subscribedStatus"}), - ] - - def __str__(self): - return "User activity by subscribed status (US)" - - -class PlaybackDetailsLiveTimeBased(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - (FeatureAmount.ZERO_OR_ONE, {"day", "month"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LIVE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Time-based playback details (live)" - - -class PlaybackDetailsViewPercentageTimeBased(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - (FeatureAmount.ZERO_OR_ONE, {"day", "month"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_VIEW_PERCENTAGE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Time-based playback details (view percentage)" - - -class PlaybackDetailsLiveGeographyBased(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"country"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LIVE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Geography-based playback details (live)" - - -class PlaybackDetailsViewPercentageGeographyBased(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"country"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_VIEW_PERCENTAGE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Geography-based playback details (view percentage)" - - -class PlaybackDetailsLiveGeographyBasedUS(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"province"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LIVE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.REQUIRED, {"country"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - - def verify(self, metrics, dimensions, filters): - super().verify(metrics, dimensions, filters) - - if filters["country"] != "US": - raise InvalidRequest("the 'country' filter must be set to 'US'") - - def __str__(self): - return "Geography-based playback details (live, US)" - - -class PlaybackDetailsViewPercentageGeographyBasedUS(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"province"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_VIEW_PERCENTAGE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.REQUIRED, {"country"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - - def verify(self, metrics, dimensions, filters): - super().verify(metrics, dimensions, filters) - - if filters["country"] != "US": - raise InvalidRequest("the 'country' filter must be set to 'US'") - - def __str__(self): - return "Geography-based playback details (view percentage, US)" - - -class PlaybackLocation(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"insightPlaybackLocationType"}), - (FeatureAmount.ANY, {"day", "liveOrOnDemand", "subscribedStatus"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus"}), - ] - - def __str__(self): - return "Playback locations" - - -class PlaybackLocationDetail(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"insightPlaybackLocationDetail"})] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.REQUIRED, {"insightPlaybackLocationType"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus"}), - ] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if filters["insightPlaybackLocationType"] != "EMBEDDED": - raise InvalidRequest("the 'insightPlaybackLocationType' filter must be set to 'EMBEDDED'") - - if not max_results or max_results >= 25: - raise InvalidRequest("the 'max_results' parameter must not be set above 25") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Playback locations (detailed)" - - -class TrafficSource(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"insightTrafficSourceType"}), - (FeatureAmount.ANY, {"day", "liveOrOnDemand", "subscribedStatus"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus"}), - ] - - def __str__(self): - return "Traffic sources" - - -class TrafficSourceDetail(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"insightTrafficSourceDetail"})] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.REQUIRED, {"insightTrafficSourceType"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus"}), - ] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if not max_results or max_results >= 25: - raise InvalidRequest("the 'max_results' parameter must not be set above 25") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Traffic sources (detailed)" - - -class DeviceType(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"deviceType"}), - (FeatureAmount.ANY, {"day", "liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"operatingSystem", "liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Device types" - - -class OperatingSystem(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"operatingSystem"}), - (FeatureAmount.ANY, {"day", "liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"deviceType", "liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Operating systems" - - -class DeviceTypeAndOperatingSystem(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"deviceType", "operatingSystem"}), - (FeatureAmount.ANY, {"day", "liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus", "youtubeProduct"}), - ] - - def __str__(self): - return "Device types and operating systems" - - -class ViewerDemographics(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.NON_ZERO, {"ageGroup", "gender"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus"}), - ] - self.metrics = ("viewerPercentage",) - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"liveOrOnDemand", "subscribedStatus"}), - ] - - def __str__(self): - return "Viewer demographics" - - -class EngagementAndContentSharing(ReportType): - def __init__(self): - self.dimensions = [ - (FeatureAmount.REQUIRED, {"sharingService"}), - (FeatureAmount.ZERO_OR_ONE, {"subscribedStatus"}), - ] - self.metrics = ("shares",) - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ANY, {"subscribedStatus"}), - ] - - def __str__(self): - return "Engagement and content sharing" - - -class AudienceRetention(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"elaspedVideoTimeRatio"})] - self.metrics = ("audienceWatchRatio", "relativeRetentionPerformance") - self.filters = [ - (FeatureAmount.REQUIRED, {"video"}), - (FeatureAmount.ANY, {"audienceType", "subscribedStatus", "youtubeProduct"}), - ] - - def verify(self, metrics, dimensions, filters): - super().verify(metrics, dimensions, filters) - - if len(filters["video"].split(",")) > 1: - raise InvalidRequest("you can only specify 1 video ID") - - def __str__(self): - return "Audience retention" - - -class TopVideosRegional(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"video"})] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_METRICS - self.filters = [(FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"})] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if not max_results or max_results >= 200: - raise InvalidRequest("the 'max_results' parameter must not be set above 200") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Top videos by region" - - -class TopVideosUS(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"video"})] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PROVINCE_METRICS - self.filters = [(FeatureAmount.REQUIRED, {"province"}), (FeatureAmount.ZERO_OR_ONE, {"subscribedStatus"})] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if not max_results or max_results >= 200: - raise InvalidRequest("the 'max_results' parameter must not be set above 200") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Top videos by state" - - -class TopVideosSubscribed(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"video"})] - self.metrics = features.YOUTUBE_ANALYTICS_SUBSCRIPTION_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"subscribedStatus"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"}), - ] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if not max_results or max_results >= 200: - raise InvalidRequest("the 'max_results' parameter must not be set above 200") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Top videos by subscription status" - - -class TopVideosYouTubeProduct(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"video"})] - self.metrics = features.YOUTUBE_ANALYTICS_VIEW_PERCENTAGE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if not max_results or max_results >= 200: - raise InvalidRequest("the 'max_results' parameter must not be set above 200") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Top videos by YouTube product" - - -class TopVideosPlaybackDetail(ReportType): - def __init__(self): - self.dimensions = [(FeatureAmount.REQUIRED, {"video"})] - self.metrics = features.YOUTUBE_ANALYTICS_LIVE_PLAYBACK_DETAIL_METRICS - self.filters = [ - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ] - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if not max_results or max_results >= 200: - raise InvalidRequest("the 'max_results' parameter must not be set above 200") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Top videos by playback detail" - - -class BasicUserActivityPlaylist(ReportType): - def __init__(self): - self.dimensions = [] - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Basic user activity for playlists" - - -class TimeBasedActivityPlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.EXACTLY_ONE, {"day", "month"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Time-based activity for playlists" - - -class GeographyBasedActivityPlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"country"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Geography-based activity for playlists" - - -class GeographyBasedActivityUSPlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"province"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated", "country"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - if filters["country"] != "US": - raise InvalidRequest("the 'country' filter must be set to 'US'") - - def __str__(self): - return "Geography-based activity for playlists (US)" - - -class PlaybackLocationPlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"insightPlaybackLocationType"}), - (FeatureAmount.ANY, {"day", "subscribedStatus"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Playback locations for playlists" - - -class PlaybackLocationDetailPlaylist(ReportType): - def __init__(self): - self.dimensions = (FeatureAmount.REQUIRED, {"insightPlaybackLocationDetail"}) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated", "insightPlaybackLocationType"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus"}), - ) - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - if filters["insightPlaybackLocationType"] != "EMBEDDED": - raise InvalidRequest("the 'insightPlaybackLocationType' filter must be set to 'EMBEDDED'") - - if not max_results or max_results >= 25: - raise InvalidRequest("the 'max_results' parameter must not be set above 25") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Playback locations for playlists (detailed)" - - -class TrafficSourcePlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"insightTrafficSourceType"}), - (FeatureAmount.ANY, {"day", "subscribedStatus"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Traffic sources for playlists" - - -class TrafficSourceDetailPlaylist(ReportType): - def __init__(self): - self.dimensions = (FeatureAmount.REQUIRED, {"insightTrafficSourceDetail"}) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated", "insightTrafficSourceType"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus"}), - ) - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - if not max_results or max_results >= 25: - raise InvalidRequest("the 'max_results' parameter must not be set above 25") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Traffic sources for playlists (detailed)" - - -class DeviceTypePlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"deviceType"}), - (FeatureAmount.ANY, {"day", "subscribedStatus", "youtubeProduct"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"operatingSystem", "subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Device types for playlists" - - -class OperatingSystemPlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"operatingSystem"}), - (FeatureAmount.ANY, {"day", "subscribedStatus", "youtubeProduct"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"deviceType", "subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Operating systems for playlists" - - -class DeviceTypeAndOperatingSystemPlaylist(ReportType): - def __init__(self): - self.dimensions = ( - (FeatureAmount.REQUIRED, {"deviceType", "operatingSystem"}), - (FeatureAmount.ANY, {"day", "subscribedStatus", "youtubeProduct"}), - ) - self.metrics = features.YOUTUBE_ANALYTICS_LOCATION_AND_TRAFFIC_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Device types and operating systems for playlists" - - -class ViewerDemographicsPlaylist(ReportType): - def __init__(self): - self.dimensions = ((FeatureAmount.NON_ZERO, {"ageGroup", "gender"}), (FeatureAmount.ANY, {"subscribedStatus"})) - self.metrics = ("viewerPercentage",) - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ZERO_OR_ONE, {"playlist", "group"}), - (FeatureAmount.ANY, {"subscribedStatus"}), - ) - - def verify(self, metrics, dimensions, filters, *args): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - def __str__(self): - return "Viewer demographics for playlists" - - -class TopPlaylists(ReportType): - def __init__(self): - self.dimensions = (FeatureAmount.REQUIRED, {"playlist"}) - self.metrics = features.YOUTUBE_ANALYTICS_ALL_PLAYLIST_METRICS - self.filters = ( - (FeatureAmount.REQUIRED, {"isCurated"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "province", "continent", "subContinent"}), - (FeatureAmount.ANY, {"playlist", "subscribedStatus", "youtubeProduct"}), - ) - - def verify(self, metrics, dimensions, filters, max_results, sort_by): - super().verify(metrics, dimensions, filters) - - if filters["isCurated"] != "1": - raise InvalidRequest("the 'isCurated' filter must be set to '1'") - - if not max_results or max_results >= 200: - raise InvalidRequest("the 'max_results' parameter must not be set above 200") - - if not sort_by: - raise InvalidRequest("you must provide at least 1 sort parameter") - - if any(not s[0] == "-" for s in sort_by): - raise InvalidRequest( - ( - "you can only sort in descending order for this report type. " - "You can do this by prefixing the sort metrics with '-'" - ) - ) - - def __str__(self): - return "Top playlists" - - -class AdPerformance(ReportType): - def __init__(self): - self.dimensions = ((FeatureAmount.REQUIRED, {"adType"}), (FeatureAmount.ANY, {"day"})) - self.metrics = ("grossRevenue", "adImpressions", "cpm") - self.filters = ( - (FeatureAmount.ZERO_OR_ONE, {"video", "group"}), - (FeatureAmount.ZERO_OR_ONE, {"country", "continent", "subContinent"}), - ) - - def __str__(self): - return "Ad performance" - - -def determine(metrics, dimensions, filters): - curated = filters.get("isCurated", "0") == "1" - - if "sharingService" in dimensions: - return EngagementAndContentSharing - - if "elapsedVideoTimeRatio" in dimensions: - return AudienceRetention - - if "playlist" in dimensions: - return TopPlaylists - - if "insightPlaybackLocationType" in dimensions: - if curated: - return PlaybackLocationPlaylist - return PlaybackLocation - - if "insightPlaybackLocationDetail" in dimensions: - if curated: - return PlaybackLocationDetailPlaylist - return PlaybackLocationDetail - - if "insightTrafficSourceType" in dimensions: - if curated: - return TrafficSourcePlaylist - return TrafficSource - - if "insightTrafficSourceDetail" in dimensions: - if curated: - return TrafficSourceDetailPlaylist - return TrafficSourceDetail - - if "ageGroup" in dimensions or "gender" in dimensions: - if curated: - return ViewerDemographicsPlaylist - return ViewerDemographics - - if "deviceType" in dimensions: - if "operatingSystem" in dimensions: - if curated: - return DeviceTypeAndOperatingSystemPlaylist - return DeviceTypeAndOperatingSystem - if curated: - return DeviceTypePlaylist - return DeviceType - - if "operatingSystem" in dimensions: - if curated: - return OperatingSystemPlaylist - return OperatingSystem - - if "video" in dimensions: - if "province" in filters: - return TopVideosUS - if "subscribedStatus" not in filters: - return TopVideosRegional - if "province" not in filters and "youtubeProduct" not in filters: - return TopVideosSubscribed - if "averageViewPercentage" in metrics: - return TopVideosYouTubeProduct - return TopVideosPlaybackDetail - - if "country" in dimensions: - if "liveOrOnDemand" in dimensions or "liveOrOnDemand" in filters: - return PlaybackDetailsLiveGeographyBased - if curated: - return GeographyBasedActivityPlaylist - if ( - "subscribedStatus" in dimensions - or "subscribedStatus" in filters - or "youtubeProduct" in dimensions - or "youtubeProduct" in filters - ): - return PlaybackDetailsViewPercentageGeographyBased - return GeographyBasedActivity - - if "province" in dimensions: - if "liveOrOnDemand" in dimensions or "liveOrOnDemand" in filters: - return PlaybackDetailsLiveGeographyBasedUS - if curated: - return GeographyBasedActivityPlaylistUS - if ( - "subscribedStatus" in dimensions - or "subscribedStatus" in filters - or "youtubeProduct" in dimensions - or "youtubeProduct" in filters - ): - return PlaybackDetailsViewPercentageGeographyBasedUS - return GeographyBasedActivityUS - - if "youtubeProduct" in dimensions or "youtubeProduct" in filters: - if "liveOrOnDemand" in dimensions or "liveOrOnDemand" in filters: - return PlaybackDetailsLiveTimeBased - return PlaybackDetailsViewPercentageTimeBased - - if "liveOrOnDemand" in dimensions or "liveOrOnDemand" in filters: - return PlaybackDetailsLiveTimeBased - - if "subscribedStatus" in dimensions: - if "province" in filters: - return PlaybackDetailsSubscribedStatusUS - return PlaybackDetailsSubscribedStatus - - if "day" in dimensions or "month" in dimensions: - if curated: - return TimeBasedActivityPlaylist - if "province" in filters: - return TimeBasedActivityUS - return TimeBasedActivity - - if curated: - return BasicUserActivityPlaylist - if "province" in filters: - return BasicUserActivityUS - return BasicUserActivity diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index ba8c36f..0000000 --- a/docs/changelog.rst +++ /dev/null @@ -1,93 +0,0 @@ -Changelog -========= - -v1.2.2 ------- - -- Verify sort metrics better -- Fix a few bugs - -v1.2.1 ------- - -Bug fixes. - -v1.2.0 ------- - -Added helper functions for retrieving report types: - -- :code:`.get_report_type()` -- :code:`.get_verified_report_type()` - -v1.1.1 ------- - -Fixed bug that meant JSON reports could not be saved. - -v1.1.0 ------- - -- Added playlist report type support. -- If no specific report type could be determined, analytix now falls back to a basic user activity report. -- A few minor efficient enhancements. - -v1.0.2 ------- - -- Fixed ordering issue. - -v1.0.1 ------- - -- Fixed an issue with report type that have required and optional dimensions. -- Fixed some stuff in the docs. - -v1.0.0 ------- - -- Corrected some bugs in rc1. -- Added some extra verification. -- Made some messages look a little nicer, especially some error messages. -- Made it impossible to install on unsupported versions. -- Corrected some issues in the docs. - -v1.0.0rc1 ---------- - -.. warning:: - - This release contains multiple breaking changes. Code written using v0.1.0 will not function from this version. - -- The number of supported video report types has increased from 6 to 29 (though playlist report types have been temporarily removed -- they'll be back in v1.1). -- There is now just one class that caters for all report types, rather than a separate class for each one. -- You no longer need to specify which scopes to use. -- :code:`start_date` is now optional. -- You can now choose to bypass request verification. -- All available metrics are used by default if none are passed by the user. - -v0.1.0 ------- - -Added support for the following report types: - -- Geographical data -- Basic user data for playlists -- Time based data for playlists -- Geographical data for playlists - -v0.1.0a2 --------- - -- Adds the :code:`YouTubeService.authorize` alias. -- Adds auto-docs. - -v0.1.0a1 --------- - -The first release on PyPI. Added the base code, and some basic classes: - -- :code:`BasicYouTubeAnalytics` -- :code:`TimeBasedYouTubeAnalytics` - -Also added the ability to save the report as a JSON or a CSV.