"""Custom report logic.""" from fastapi import HTTPException from moneyhub import models from moneyhub.config import Config from moneyhub.connectors import sqs from moneyhub.connectors.s3 import create_presigned_url from moneyhub.constants.constants import NumberFormat from moneyhub.constants.constants import ReportCustomFileType from moneyhub.constants.constants import ReportCustomStatus from moneyhub.constants.constants import RevenueDisplayType from moneyhub.constants.error import NO_REPORT_FILE_LOCATION from moneyhub.constants.error import STATEMENT_DATA_NOT_AVAILABLE from moneyhub.schemas import ReportCustomCreateSchema from moneyhub.utils.aws import parse_s3_url from moneyhub.utils.request import extract_json_body def create_report( account_id: int, payload: ReportCustomCreateSchema, orchard_identity_id: str, correlation_id: str | None = None ) -> models.ReportCustom: """Create a custom report attachment. Args: account_id (int): Account to create the report for payload (ReportCustomCreateSchema): JSON body payload orchard_identity_id (str): orchard identity id correlation_id (str): Optional request correlation ID Returns: ReportCustom: The created report """ body = extract_json_body(payload) statement_period_ids = sorted(set(body['statement_period_ids'])) _check_statement_periods(account_id, statement_period_ids) report = models.ReportCustom.build( account_id=account_id, contract_id=body.get('contract_id'), subaccount_id=body.get('subaccount_id'), statement_period_ids=','.join(str(i) for i in statement_period_ids), revenue_type=body['revenue_type'], revenue_display_type=body.get('revenue_display_type', RevenueDisplayType.NET), dimension_column=body['dimension_column'], dimension_row=body['dimension_row'], filters=body.get('filters'), number_format=body.get('number_format', NumberFormat.US), file_type=body.get('file_type', ReportCustomFileType.CSV), report_custom_status=ReportCustomStatus.IN_PROGRESS, created_by=orchard_identity_id ) models.ReportCustom.commit_changes() sqs.send_message( Config.SQS_MH_CUSTOM_REPORTS_QUEUE_NAME, correlation_id, {'report_custom_id': report.report_custom_id} ) return report def _check_statement_periods(account_id: int, statement_period_ids: list) -> None: visible_periods, _ = models.AccountStatementPeriods.get_by_account_id(account_id, None) visible_periods = [row.statement_period_id for row in visible_periods] for statement_period_id in statement_period_ids: if statement_period_id not in visible_periods: raise HTTPException( status_code=400, detail=STATEMENT_DATA_NOT_AVAILABLE) def get_custom_report(report_custom_id: int) -> models.ReportCustom: """Get custom report by ID. Args: report_custom_id (int): ID of the report Returns: ReportCustom: the report """ return models.ReportCustom.get_by_id_or_error(report_custom_id) def get_custom_reports( account_id: int, contract_id: int | None, subaccount_id: int | None = None, is_subaccount: bool = False ) -> list[models.ReportCustom]: """Get custom reports by an account_id. Args: account_id (int): The id of an account contract_id (int): Contract to filter by subaccount_id (int): Optional subaccount to filter by is_subaccount (bool): Whether to filter by subaccount_id Returns: list: list of custom reports """ return models.ReportCustom.get_by_account_id( account_id, contract_id, subaccount_id, is_subaccount ) def get_report_presigned_url(report_custom_id: int) -> dict: """Get presigned S3 URL for downloading custom report. Args: report_custom_id (int): The id of custom report Returns: dict: presigned url of report file location """ custom_report = models.ReportCustom.get_by_id_or_error( report_custom_id) file_location = custom_report.file_location if not file_location: raise Exception( NO_REPORT_FILE_LOCATION.format( report_custom_id=report_custom_id ) ) bucket, key = parse_s3_url(file_location) presigned_url = create_presigned_url(bucket, key) return presigned_url def update_custom_report( report_custom_id: int, report_data: dict) -> models.ReportCustom: """Update custom report by a report_custom_id. Args: report_custom_id (int): The id of a report report_data (list): List of properties to update Returns: ReportCustom: Updated custom report """ return models.ReportCustom.update_custom_report( report_custom_id, report_data) def delete_custom_report(report_custom_id: int) -> models.ReportCustom: """Delete custom report by ID. Args: report_custom_id (int): ID of the report Returns: ReportCustom: Deleted custom report """ return models.ReportCustom.delete_by_id(report_custom_id) def regenerate_custom_reports( statement_period_id: int, correlation_id: str | None = None) -> list: """Regenerate custom reports. Args: statement_period_id (int): Statement period to regenerate reports for Returns: list: reports which will be regenerated """ reports = models.ReportCustom.get_by_statement_period(statement_period_id, None) if len(reports) == 0: return [] for report in reports: report.report_custom_status = ReportCustomStatus.IN_PROGRESS models.ReportCustom.commit_changes() messages = [ {'report_custom_id': report.report_custom_id} for report in reports ] sqs.send_messages( Config.SQS_MH_CUSTOM_REPORTS_QUEUE_NAME, correlation_id, messages) return reports