""" REPORT GENERATION TASKS Celery tasks for generating an audit report. The find() task is a periodic task (like a cron). It is meant to be run as a single process that will spawn other asynchronous tasks in this file. This is accomplished via cache locking method described in celery docs: http://celery.readthedocs.org/en/latest/tutorials/ task-cookbook.html#cookbook-task-serial """ from celery import Celery from kombu import Exchange, Queue import os from datetime import timedelta from labelaudit.connectors import sentry from labelaudit import config from labelaudit.logic import audit_persistence from labelaudit.tasks.task_lock import TaskLock from labelaudit.logic import report_generation sentry # pep8 app = Celery('label_audit_cron_app') app.config_from_object(dict( BROKER_URL=config.BROKER_URL, CELERY_DEFAULT_QUEUE=config.AUDIT_GENERATION_QUEUE, CELERY_ACCEPT_CONTENT=['pickle'], CELERY_QUEUES=(Queue( config.AUDIT_GENERATION_QUEUE, Exchange(config.AUDIT_GENERATION_QUEUE), routing_key='labelaudit.tasks.report_generation_tasks.#'),), CELERYBEAT_SCHEDULE={ 'run_every_5_minutes': { 'task': 'labelaudit.tasks.report_generation_tasks.find', 'schedule': timedelta( seconds=int( os.environ.get('AUDIT_GENERATION_RUN_INTERVAL') or 300)), }, }, CELERY_TIMEZONE='UTC', CELERY_ENABLE_UTC=True, BROKER_TRANSPORT_OPTIONS=dict( queue_name_prefix=config.CELERY_QUEUE_PREFIX))) @app.task( name='labelaudit.tasks.report_generation_tasks.find', queue=config.AUDIT_GENERATION_QUEUE) def find(): """The main workflow of the periodic audit completion job. Runs every few minutes to check for a new batch of audit reports which have all the data ready to be generated into a CSV file. This is a periodic task and each time it processes one batch. Each report generation is kicked off asynchronously and then the status of the report is set to 'generating'. Task will retry if there is an error trying to get the reports. """ # Cache is not optional for this, so alert if it's off if not config.CACHE_ENABLED: raise Exception( 'You must enable the cache (CACHE_ENABLED) ' 'to run the periodic find() task.') lock = TaskLock('labelaudit.tasks.report_generation_tasks.find') if lock.acquire_task(): try: limit = config.AUDIT_GENERATION_BATCH_SIZE results = audit_persistence.fetch_all( allReleasesStatus='complete', auditStatus='in_progress', limit=limit, offset=0) reports = results.message.get('reports') or [] for report in reports: generate.delay(report) # Update the status of reports synchronously to avoid # unnecessarily rerunning the reports just kicked off above. audit_persistence.update_report_status( report.get('youtubeAuditId'), 'generating') # TODO add except that will use log/alert system for exceptions finally: lock.delete() else: # TODO convert to logger once we have decided on logging/alerts. print('Audit completion periodic task already running.') @app.task( name='labelaudit.tasks.report_generation_tasks.generate', queue=config.AUDIT_GENERATION_QUEUE) def generate(report): """Generate a CSV audit report and drop in S3 bucket. This will create a CSV report then call task to drop it on S3. This should be idempotent and replace a previous report if called to be run again. On completion of the copy to S3, the status of the report will be moved to 'complete'. Args: report (dict): data for a single report EX: { 'auditStatus': 'in_progress', 'initiatedById': '888', 'reportLocation': 'some location', 'updatedTimestamp': '2015-06-09 14:30:36', 'vendorId': '5', 'youtubeAuditId': '3' } """ report_generation.generate(report)