1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

# Copyright 2013 The Distro Tracker Developers 

# See the COPYRIGHT file at the top-level directory of this distribution and 

# at https://deb.li/DTAuthors 

# 

# This file is part of Distro Tracker. It is subject to the license terms 

# in the LICENSE file found in the top-level directory of this 

# distribution and at https://deb.li/DTLicense. No part of Distro Tracker, 

# including this file, may be copied, modified, propagated, or distributed 

# except according to the terms contained in the LICENSE file. 

""" 

Defines models specific for the :py:mod:`distro_tracker.mail` app. 

""" 

from django.conf import settings 

from django.db import models 

 

from distro_tracker.core.models import ( 

Confirmation, 

ConfirmationManager, 

UserEmail 

) 

 

from django_email_accounts.models import UserEmailManager 

 

 

class CommandConfirmationManager(ConfirmationManager): 

""" 

A custom manager for the :py:class:`CommandConfirmation` model. 

""" 

def create_for_commands(self, commands): 

""" 

Creates a :py:class:`CommandConfirmation` object for the given commands. 

 

:param commands: An iterable of commands for which a confirmation is 

requested. 

:raises distro_tracker.mail.models.CommandConfirmationException: If it 

is unable to generate a unique key. 

""" 

commands = '\n'.join(commands) 

return self.create_confirmation(commands, **{ 

'commands': commands, 

}) 

 

 

class CommandConfirmation(Confirmation): 

""" 

A model representing pending confirmations for email interface commands. 

""" 

commands = models.TextField() 

 

objects = CommandConfirmationManager() 

 

def __str__(self): 

return self.commands 

 

@property 

def command_list(self): 

""" 

:return: A list of strings representing commands which are confirmed 

by this instance. 

""" 

return self.commands.splitlines() 

 

 

class UserEmailBounceStatsManager(UserEmailManager): 

""" 

A custom :py:class:`Manager <django.db.models.Manager>` for the 

:py:class:`UserEmailBounceStats` model. 

""" 

def get_bounce_stats(self, email, date): 

""" 

Gets the :py:class:`UserEmailBounceStats` instance for the given 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` on the 

given ``date`` 

 

:param email: The email of the 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` 

:type email: string 

 

:param date: The date of the required stats 

:type date: :py:class:`datetime.datetime` 

""" 

user = self.get(email__iexact=email) 

bounce_stats, created = user.bouncestats_set.get_or_create(date=date) 

if created: 

self.limit_bounce_information(email) 

return bounce_stats 

 

def add_bounce_for_user(self, email, date): 

""" 

Registers a bounced email for a given 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` 

 

:param email: The email of the 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` for 

which a bounce will be logged 

:type email: string 

 

:param date: The date of the bounce 

:type date: :py:class:`datetime.datetime` 

""" 

bounce_stats = self.get_bounce_stats(email, date) 

bounce_stats.mails_bounced += 1 

bounce_stats.save() 

 

def add_sent_for_user(self, email, date): 

""" 

Registers a sent email for a given 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` 

 

:param email: The email of the 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` for 

which a sent email will be logged 

:type email: string 

 

:param date: The date of the sent email 

:type date: :py:class:`datetime.datetime` 

""" 

bounce_stats = self.get_bounce_stats(email, date) 

bounce_stats.mails_sent += 1 

bounce_stats.save() 

 

def limit_bounce_information(self, email): 

""" 

Makes sure not to keep more records than the number of days set by 

DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE 

""" 

user = self.get(email__iexact=email) 

days = settings.DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE 

for info in user.bouncestats_set.all()[days:]: 

info.delete() 

 

 

class UserEmailBounceStats(UserEmail): 

""" 

A proxy model for the :py:class:`UserEmail 

<distro_tracker.core.models.UserEmail>` model. 

It is defined in order to implement additional bounce stats-related 

methods without needlessly adding them to the public interface of 

:py:class:`UserEmail <distro_tracker.core.models.UserEmail>` when only the 

:py:mod:`distro_tracker.mail.dispatch` app should use them. 

""" 

class Meta: 

proxy = True 

 

objects = UserEmailBounceStatsManager() 

 

def has_too_many_bounces(self): 

""" 

Checks if the user has too many bounces. 

""" 

days = settings.DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE 

count = 0 

for stats in self.bouncestats_set.all()[:days]: 

# If no mails were sent on a particular day nothing could bounce 

if stats.mails_sent: 

if stats.mails_bounced >= stats.mails_sent: 

count += 1 

return count == days 

 

 

class BounceStats(models.Model): 

""" 

A model representing a user's bounce statistics. 

 

It stores the number of sent and bounced mails for a particular date. 

""" 

user_email = models.ForeignKey(UserEmailBounceStats, 

on_delete=models.CASCADE) 

mails_sent = models.IntegerField(default=0) 

mails_bounced = models.IntegerField(default=0) 

date = models.DateField() 

 

class Meta: 

ordering = ['-date'] 

unique_together = ('user_email', 'date') 

 

def __str__(self): 

return ( 

'Got {bounced} bounces out of {sent} ' 

'mails to {email} on {date}'.format( 

email=self.user_email, 

date=self.date, 

sent=self.mails_sent, 

bounced=self.mails_bounced) 

)