Coverage for functions \ flipdare \ task \ report_task_handler.py: 78%

77 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-05-08 12:22 +1000

1#!/usr/bin/env python 

2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved. 

3# 

4# This file is part of Flipdare's proprietary software and contains 

5# confidential and copyrighted material. Unauthorised copying, 

6# modification, distribution, or use of this file is strictly 

7# prohibited without prior written permission from Flipdare Pty Ltd. 

8# 

9# This software includes third-party components licensed under MIT, 

10# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details. 

11# 

12 

13from __future__ import annotations 

14 

15from typing import TYPE_CHECKING 

16from collections.abc import Sequence 

17from flipdare.app_log import LOG 

18from flipdare.constants import IS_DEBUG 

19from flipdare.mailer.admin.app_log_email import AppLogEmail 

20from flipdare.generated.schema.email.body.admin.log_email_schema import LogEmailSchema 

21from flipdare.generated.shared.backend.system_log_type import SystemLogType 

22from flipdare.service._service_provider import ServiceProvider 

23from flipdare.service.core.cron_processor import CronResultEntry 

24from flipdare.task.report.stats_reporter import StatsReporter 

25from flipdare.task.report.core.cron_table_report import CronTableReport 

26from flipdare.task.report.dare_reporter import DareReporter 

27from flipdare.task.report.flag_reporter import FlagReporter 

28from flipdare.task.report.issue_reporter import IssueReporter 

29from flipdare.task.report.log_reporter import LogReporter 

30from flipdare.task.report.payment_issue_reporter import PaymentIssueReporter 

31from flipdare.task.report.restriction_reporter import RestrictionReporter 

32from flipdare.generated.shared.backend.app_job_type import AppJobType 

33from flipdare.result.output_result import OutputResult 

34from flipdare.job_types import CronWithResultType, ReportJobType 

35from flipdare.util.time_util import TimeUtil 

36 

37if TYPE_CHECKING: 

38 from flipdare.manager.db_manager import DbManager 

39 from flipdare.manager.backend_manager import BackendManager 

40 

41 

42__all__ = ["ReportTaskHandler"] 

43 

44 

45class ReportTaskHandler(ServiceProvider): 

46 # NOTE: we need access to everything, so we defy the standard 

47 # NOTE: for a core admin class. 

48 def __init__( 

49 self, 

50 db_manager: DbManager | None = None, 

51 system_manager: BackendManager | None = None, 

52 ) -> None: 

53 super().__init__( 

54 db_manager=db_manager, 

55 backend_manager=system_manager, 

56 ) 

57 

58 def run_report(self, job_type: ReportJobType) -> OutputResult: # noqa: PLR0912 

59 db = self.db_manager 

60 sys = self.backend_manager 

61 result: OutputResult 

62 

63 match job_type: 

64 case AppJobType.REPORT_DARE_REVIEW_REQUIRED: 

65 result = DareReporter(db, sys).review_required() 

66 case AppJobType.REPORT_DARE_AUTO_RESTRICTED: 

67 result = DareReporter(db, sys).auto_restricted() 

68 case AppJobType.REPORT_ISSUE_WAITING_ADMIN: 

69 result = IssueReporter(db, sys).issue_waiting_admin() 

70 case AppJobType.REPORT_PAYMENT_ISSUE_WAITING_ADMIN: 

71 result = PaymentIssueReporter(db, sys).issue_waiting_admin() 

72 case AppJobType.REPORT_FLAG_UNACKNOWLEDGED: 

73 result = FlagReporter(db, sys).unacknowledged() 

74 case AppJobType.REPORT_FLAG_DISPUTED_WAITING_ADMIN: 

75 result = FlagReporter(db, sys).waiting_disputed() 

76 case AppJobType.REPORT_RESTRICT_AUTO_PERMANENT: 

77 result = RestrictionReporter(db, sys).auto_permanent() 

78 case AppJobType.REPORT_RESTRICT_AUTO_NOT_PERMANENT: 

79 result = RestrictionReporter(db, sys).auto_not_permanent() 

80 case AppJobType.REPORT_RESTRICT_INACTIVE: 

81 result = RestrictionReporter(db, sys).inactive() 

82 case AppJobType.REPORT_PAYMENT_CRITICAL_ISSUES: 

83 result = LogReporter(db, sys).payment_critical_issues() 

84 case AppJobType.REPORT_JOB_TYPE_STATS: 

85 result = StatsReporter(db, sys).job_stats() 

86 case AppJobType.REPORT_LOG_STATS: 

87 result = StatsReporter(db, sys).log_stats() 

88 case AppJobType.REPORT_ERROR_STATS: 

89 result = StatsReporter(db, sys).error_stats() 

90 case AppJobType.REPORT_PAYMENT_STATS: 

91 result = StatsReporter(db, sys).payment_stats() 

92 

93 if result.is_error: 

94 self._log_report_error(job_type, result) 

95 

96 return result 

97 

98 def run_cron_with_result( 

99 self, 

100 job_type: CronWithResultType, 

101 processed: Sequence[CronResultEntry], 

102 ) -> OutputResult: 

103 report = CronTableReport( 

104 job_type=job_type, 

105 processed=processed, 

106 app_logger=self.app_logger, 

107 mailer=self.admin_mailer, 

108 ) 

109 

110 result = report.create_and_send() 

111 if result.is_error: 

112 self._log_report_error(job_type, result) 

113 

114 return result 

115 

116 def _log_report_error(self, job_type: AppJobType, result: OutputResult) -> None: 

117 if result.is_ok: 

118 if IS_DEBUG: 

119 LOG().debug( 

120 f"Report {job_type.value} completed successfully with message: {result.message}" 

121 ) 

122 return 

123 

124 msg = ( 

125 f"Report {job_type.value} failed to generate or send email. Message: {result.message}" 

126 ) 

127 LOG().error(msg) 

128 

129 log_schema: LogEmailSchema = { 

130 "log_type": SystemLogType.ERROR, 

131 "called_from": f"ReportTaskHandler.run_report for {job_type.value}", 

132 "source": job_type.value, 

133 "message": msg, 

134 "occurred_at": TimeUtil.formatted_user(TimeUtil.get_current_utc_dt()), 

135 "job_type": job_type, 

136 } 

137 

138 log_email = AppLogEmail(data=log_schema) 

139 self.admin_mailer.send(log_email)