"""Youtube feeds specific util functions.""" import datetime import gzip import os import re from googleapiclient.discovery import build from googleapiclient.http import MediaIoBaseDownload import httplib2 from oauth2client.file import Storage from feed_ingestion.util import os_tools def get_days(date): """Get download, start and end dates of ingestion. Args: date (str): Reporting date (YYYY-MM-DD). Returns: tuple(datetime, datetime, datetime): Download day, start day, end day. """ date_obj = datetime.datetime.strptime(date, '%Y-%m-%d').date() start_date = date_obj end_date = date_obj + datetime.timedelta(days=6) return date_obj, start_date, end_date def get_authenticated_services( credentials_path, api_service_name, api_version): """Get YouTube API authenticated service. Args: credentials_path (str): Path to credentials file. api_service_name (str): YouTube API service name. api_version (str): YouTube API version. Returns: Resource: A Resource object with methods for interacting with the service. """ storage = Storage(credentials_path) credentials = storage.get() if credentials is None: raise Exception('Valid YouTube API credentials file required.') http = credentials.authorize(httplib2.Http()) return build(api_service_name, api_version, http=http) def get_url(content_owner, job_id, start_date, youtube_reports): """Make an HTTP API call to the YT API. Checks if an report exists upstream, and returns a DL url. Args: content_owner (str): Content owner id. job_id (str): Report id. start_date (str): Report date. youtube_reports (Resource): YouTube API object. Returns: str: Report url. """ date_obj = datetime.datetime.strptime(start_date, '%Y-%m-%d') start_time = date_obj.strftime('%Y-%m-%dT%H:%M:%S.%fZ') end_time = ( date_obj + datetime.timedelta(days=1)).strftime( '%Y-%m-%dT%H:%M:%S.%fZ') target_report = youtube_reports.jobs().reports().list( onBehalfOfContentOwner=content_owner, jobId=job_id, startTimeAtOrAfter=start_time, startTimeBefore=end_time).execute() if not target_report: raise ValueError( 'Report not available. Job id: {}. Date: {}'.format( job_id, start_time)) return target_report['reports'][0]['downloadUrl'] def get_job(content_owner, jobs, report_name): """Get job description from jobs list. Args: content_owner (str): Content owner id. jobs (list): List of available jobs from jobs file. Generated by YouTube. report_name (str): Name of the report to ingest. """ pattern = re.compile( r'^{report_name}.*'.format(report_name=report_name)) jobs_list = [j for j in jobs if j['content_owner'] == content_owner and re.match(pattern, j['job_name'])] if not jobs_list: raise Exception('Invalid report name {}.'.format(report_name)) return jobs_list[0] def grab_reports_files_for_content_owner( content_owner, content_owner_id, job, date, youtube_reports, report_name, feed_name, gz=True): """Download reports files and archive on S3 for a single content owner. Args: content_owner (str): Content owner name. content_owner_id (str): Content owner id. job (dict): Job description. date (str): Reporting date (YYYY-MM). youtube_reports (Resource): YouTube API object. report_name (str): Name of the report to ingest. feed_name (str): flow Feed name. """ # 100mb chunks to aviod HttpError 429 when requesting # https://youtubereporting.googleapis.com/[...]?alt=media returned # "Quota exceeded for quota metric 'Free requests' and limit # 'Free requests per minute' of service 'youtubereporting.googleapis.com' CHUNK_SIZE = 200 * 1024 * 1024 url = get_url( content_owner_id, job['job_id'], date, youtube_reports) report_filename_out = '{}.{}.csv.gz'.format( report_name, content_owner) local_dir = os_tools.create_temp_dir(feed_name=feed_name, date=date) local_file_path = os.path.join(local_dir, report_filename_out) request = youtube_reports.media().download(resourceName=' ') request.uri = url if gz is True: with gzip.open(local_file_path, 'wb') as f_out: downloader = MediaIoBaseDownload( f_out, request, chunksize=CHUNK_SIZE) done = False while not done: _, done = downloader.next_chunk() else: with open(local_file_path, 'wb') as f_out: downloader = MediaIoBaseDownload( f_out, request, chunksize=CHUNK_SIZE) done = False while not done: _, done = downloader.next_chunk() return local_dir, local_file_path