Coverage for apps/communications/schemas.py: 100%

89 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-11-04 14:06 +0000

1from __future__ import annotations 

2 

3from ninja import ModelSchema, Schema 

4 

5from apps.communications.models import Communication, MessageTemplate 

6 

7 

8class MessageTemplateOut(ModelSchema): 

9 class Meta: 

10 model = MessageTemplate 

11 fields = [ 

12 "id", 

13 "name", 

14 "channel", 

15 "category", 

16 "subject", 

17 "body", 

18 "description", 

19 "trigger", 

20 "auto_send", 

21 "active", 

22 "system", 

23 ] 

24 

25 

26class TemplateInput(Schema): 

27 name: str 

28 channel: str 

29 category: str = "Custom" 

30 subject: str | None = None 

31 body: str 

32 

33 

34class TemplatePatch(Schema): 

35 name: str | None = None 

36 category: str | None = None 

37 subject: str | None = None 

38 body: str | None = None 

39 description: str | None = None 

40 trigger: str | None = None 

41 auto_send: bool | None = None 

42 active: bool | None = None 

43 

44 

45class TemplatePreviewIn(Schema): 

46 loan_guid: str | None = None 

47 

48 

49class TemplatePreviewOut(Schema): 

50 subject: str | None 

51 body: str 

52 missing: list[str] = [] 

53 

54 

55class CommEventOut(Schema): 

56 id: int 

57 communication_id: int 

58 type: str 

59 at: str 

60 

61 

62class CommunicationOut(ModelSchema): 

63 loan_guid: str | None = None 

64 loan_number: str | None = None 

65 template_id: int | None = None 

66 

67 class Meta: 

68 model = Communication 

69 fields = [ 

70 "id", 

71 "channel", 

72 "direction", 

73 "to", 

74 "subject", 

75 "body", 

76 "status", 

77 "provider", 

78 "provider_message_id", 

79 "automated", 

80 "trigger", 

81 "created_at", 

82 "updated_at", 

83 ] 

84 

85 @staticmethod 

86 def resolve_loan_guid(obj: Communication) -> str | None: 

87 return str(obj.loan.guid) if obj.loan else None 

88 

89 @staticmethod 

90 def resolve_loan_number(obj: Communication) -> str | None: 

91 return obj.loan.arive_loan_no if obj.loan else None 

92 

93 @staticmethod 

94 def resolve_template_id(obj: Communication) -> int | None: 

95 return obj.template_id 

96 

97 

98class CommunicationDetailOut(Schema): 

99 communication: CommunicationOut 

100 events: list[CommEventOut] 

101 

102 

103class SendCommunicationInput(Schema): 

104 channel: str 

105 to: str | None = None 

106 template_id: int | None = None 

107 subject: str | None = None 

108 body: str | None = None 

109 loan_guid: str | None = None 

110 

111 

112class CommEventInput(Schema): 

113 type: str 

114 

115 

116class AutoSendPatch(Schema): 

117 auto_send: bool 

118 

119 

120class MergeFieldOut(Schema): 

121 key: str 

122 label: str 

123 sample: str 

124 

125 

126class EmailStatsOut(Schema): 

127 sent: int 

128 delivered: int 

129 opened: int 

130 clicked: int 

131 open_rate: int 

132 click_rate: int 

133 

134 

135class SmsStatsOut(Schema): 

136 sent: int 

137 delivered: int 

138 delivery_rate: int 

139 

140 

141class VoiceStatsOut(Schema): 

142 placed: int 

143 answered: int 

144 voicemail: int 

145 answer_rate: int 

146 

147 

148class CommStatsOut(Schema): 

149 total: int 

150 by_channel: dict[str, int] 

151 email: EmailStatsOut 

152 sms: SmsStatsOut 

153 voice: VoiceStatsOut