""" Audit Persistence Logic ================= This logic handles fetching, creating, and updating audits via VAPI. This code strictly handles persistence. Any additional logic (for example, adding a new audit to a queue) should be handled in separate modules that call this one. """ from labelaudit.connectors import api_client as api_connector from labelaudit.logic.result import Result def fetch(audit_id): """Calls VAPI to fetch an audit. Args: api_client (labelaudit.lib.vapi.api_client.ApiClient): VAPI client audit_id (int): ID of the audit record to fetch Return: labelaudit.logic.result.Result: object representing the outcome. message (dict) audit data returned by VAPI errors (dict) any errors returned by VAPI status (int) status code """ response = api_connector.get_client().get('youtubeaudit/' + str(audit_id)) response_body = response.json() or {} result = Result(status=response.status_code) if response.status_code == 200: result.message = response_body.get('youtubeAudit') else: result.errors = response_body.get('fault') return result def fetch_all(**kwargs): """Calls VAPI for all audit reports based on search criteria. Args: auditStatus (str): corresponds to youtube_audit.audit_status db field vendorId (int): vendor_id of vendor table allReleasesStatus (str): the status every release on the report must havecorresponds to youtube_audit_release.audit_status db field offset (int): starting number of items in series you wish to retrieve limit (int): max records to retrieve each call Return: labelaudit.logic.result.Result (object): message property will contain results as dict. Ex: {'reports': [{ 'auditStatus': 'in_progress', 'initiatedById': None, 'reportLocation': None, 'createdTimestamp': '2015-06-09 10:03:42', 'updatedTimestamp': '2015-06-09 14:30:34', 'vendorId': '22294', 'youtubeAuditId': '2' }, { 'auditStatus': 'in_progress', 'initiatedById': '888', 'reportLocation': 'some location', 'createdTimestamp': '2015-06-09 13:04:22', 'updatedTimestamp': '2015-06-09 14:30:36', 'vendorId': '5', 'youtubeAuditId': '3' }], 'total_reports': 3} error property contains VAPI faults as dict """ params = {'params': kwargs} response = api_connector.get_client().get('youtubeaudit/', **params) assert response.status_code != 500, 'Error from VAPI: ' + response.text response_body = response.json() result = Result(status=response.status_code) if response.status_code == 200: result.message = { 'total_reports': response_body.get('pagination').get('totalRecords') } result.message.update(reports=response_body.get('youtubeAudits')) else: result.errors = response_body.get('fault') return result # @todo: refactor to pass in a dict instead of a model def persist(label_audit): """Calls VAPI to persist a new or modified audit. Args: label_audit (labelaudit.models.label_audit.LabelAudit): audit object Return: labelaudit.logic.result.Result: object representing the outcome. message (dict) audit data returned by VAPI errors (dict) any errors returned by VAPI status (int) status code """ audit_data = { 'auditStatus': label_audit.audit_status, 'initiatedById': label_audit.initiated_by_id, 'reportLocation': label_audit.report_location, 'vendorId': label_audit.vendor_id } return execute_persist(audit_data, label_audit.youtube_audit_id) def execute_persist(audit_data, youtube_audit_id=None): """Call VAPI to save or update a label audit. Args: audit_data (dict): Data to save or update youtube_audit_id (int): Used to find record to update Return: labelaudit.logic.result.Result object with: message (dict) audit data returned by VAPI errors (dict) any errors returned by VAPI status (int) status code """ vapi_path = 'youtubeaudit' method = 'post' if youtube_audit_id: method = 'put' vapi_path += '/' + str(youtube_audit_id) api_client = api_connector.get_client() response = api_client.request(method, vapi_path, json=audit_data) result = Result(message=None, status=response.status_code) response_body = response.json() if response.status_code in [200, 201]: # @todo: raise exception for malformed response if no youtubeAudit result.message = response_body.get('youtubeAudit') or {} else: result.errors = response_body.get('fault') return result def update(youtube_audit_id, audit_data): """Calls VAPI to update aka patch partial data in an audit. Args: youtube_audit_id (int): youtube_audit table id audit_data (dict): only key/values you want to update Return: labelaudit.logic.result.Result: message (dict) audit data from VAPI (includes all attributes) errors (dict) any errors returned by VAPI status (int) status code """ # check if an invalid key passed for update error_res = validate_fields(audit_data) if error_res: return Result(audit_data, error_res, 422) return execute_persist(audit_data, youtube_audit_id) def update_report_status(youtube_audit_id, status): """Update the audit report status for a report. Convenience method for common update task. Parameter validation is handled on VAPI side and will cause update to fail and this method to return False. Args: youtube_audit_id (int) status (str): ex: requested, in_progress, generating, complete, error Return: boolean """ res = update(youtube_audit_id, {'auditStatus': status}) if res.errors: # TODO logging or alert once we have this return False return True def validate_fields(audit_data): """Light validation of label audit keys. Args: audit_data (dict): see 'valid_keys' for keys Return: dict of errors or None """ valid_keys = [ 'youtubeAuditId', 'auditStatus', 'initiatedById', 'reportLocation', 'vendorId'] invalid_keys = {} for key in audit_data.keys(): if key not in valid_keys: invalid_keys.update({key: 'Invalid Field Name'}) if invalid_keys: return invalid_keys return None