models.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # Copyright 2013 The Distro Tracker Developers
  2. # See the COPYRIGHT file at the top-level directory of this distribution and
  3. # at http://deb.li/DTAuthors
  4. #
  5. # This file is part of Distro Tracker. It is subject to the license terms
  6. # in the LICENSE file found in the top-level directory of this
  7. # distribution and at http://deb.li/DTLicense. No part of Distro Tracker,
  8. # including this file, may be copied, modified, propagated, or distributed
  9. # except according to the terms contained in the LICENSE file.
  10. """
  11. Defines models specific for the :py:mod:`distro_tracker.mail` app.
  12. """
  13. from __future__ import unicode_literals
  14. from django.db import models
  15. from django.conf import settings
  16. from django.utils.encoding import python_2_unicode_compatible
  17. from django_email_accounts.models import UserEmailManager
  18. from distro_tracker.core.models import UserEmail
  19. from distro_tracker.core.models import Confirmation, ConfirmationManager
  20. class CommandConfirmationManager(ConfirmationManager):
  21. """
  22. A custom manager for the :py:class:`CommandConfirmation` model.
  23. """
  24. def create_for_commands(self, commands):
  25. """
  26. Creates a :py:class:`CommandConfirmation` object for the given commands.
  27. :param commands: An iterable of commands for which a confirmation is
  28. requested.
  29. :raises distro_tracker.mail.models.CommandConfirmationException: If it
  30. is unable to generate a unique key.
  31. """
  32. commands = '\n'.join(commands)
  33. return self.create_confirmation(commands, **{
  34. 'commands': commands,
  35. })
  36. @python_2_unicode_compatible
  37. class CommandConfirmation(Confirmation):
  38. """
  39. A model representing pending confirmations for email interface commands.
  40. """
  41. commands = models.TextField()
  42. objects = CommandConfirmationManager()
  43. def __str__(self):
  44. return self.commands
  45. @property
  46. def command_list(self):
  47. """
  48. :return: A list of strings representing commands which are confirmed
  49. by this instance.
  50. """
  51. return self.commands.splitlines()
  52. class UserEmailBounceStatsManager(UserEmailManager):
  53. """
  54. A custom :py:class:`Manager <django.db.models.Manager>` for the
  55. :py:class:`UserEmailBounceStats` model.
  56. """
  57. def get_bounce_stats(self, email, date):
  58. """
  59. Gets the :py:class:`UserEmailBounceStats` instance for the given
  60. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>` on the
  61. given ``date``
  62. :param email: The email of the
  63. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>`
  64. :type email: string
  65. :param date: The date of the required stats
  66. :type date: :py:class:`datetime.datetime`
  67. """
  68. user = self.get(email__iexact=email)
  69. bounce_stats, created = user.bouncestats_set.get_or_create(date=date)
  70. if created:
  71. self.limit_bounce_information(email)
  72. return bounce_stats
  73. def add_bounce_for_user(self, email, date):
  74. """
  75. Registers a bounced email for a given
  76. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>`
  77. :param email: The email of the
  78. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>` for
  79. which a bounce will be logged
  80. :type email: string
  81. :param date: The date of the bounce
  82. :type date: :py:class:`datetime.datetime`
  83. """
  84. bounce_stats = self.get_bounce_stats(email, date)
  85. bounce_stats.mails_bounced += 1
  86. bounce_stats.save()
  87. def add_sent_for_user(self, email, date):
  88. """
  89. Registers a sent email for a given
  90. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>`
  91. :param email: The email of the
  92. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>` for
  93. which a sent email will be logged
  94. :type email: string
  95. :param date: The date of the sent email
  96. :type date: :py:class:`datetime.datetime`
  97. """
  98. bounce_stats = self.get_bounce_stats(email, date)
  99. bounce_stats.mails_sent += 1
  100. bounce_stats.save()
  101. def limit_bounce_information(self, email):
  102. """
  103. Makes sure not to keep more records than the number of days set by
  104. DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE
  105. """
  106. user = self.get(email__iexact=email)
  107. days = settings.DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE
  108. for info in user.bouncestats_set.all()[days:]:
  109. info.delete()
  110. class UserEmailBounceStats(UserEmail):
  111. """
  112. A proxy model for the :py:class:`UserEmail
  113. <distro_tracker.core.models.UserEmail>` model.
  114. It is defined in order to implement additional bounce stats-related
  115. methods without needlessly adding them to the public interface of
  116. :py:class:`UserEmail <distro_tracker.core.models.UserEmail>` when only the
  117. :py:mod:`distro_tracker.mail.dispatch` app should use them.
  118. """
  119. class Meta:
  120. proxy = True
  121. objects = UserEmailBounceStatsManager()
  122. def has_too_many_bounces(self):
  123. """
  124. Checks if the user has too many bounces.
  125. """
  126. days = settings.DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE
  127. count = 0
  128. for stats in self.bouncestats_set.all()[:days]:
  129. # If no mails were sent on a particular day nothing could bounce
  130. if stats.mails_sent:
  131. if stats.mails_bounced >= stats.mails_sent:
  132. count += 1
  133. return count == days
  134. @python_2_unicode_compatible
  135. class BounceStats(models.Model):
  136. """
  137. A model representing a user's bounce statistics.
  138. It stores the number of sent and bounced mails for a particular date.
  139. """
  140. user_email = models.ForeignKey(UserEmailBounceStats)
  141. mails_sent = models.IntegerField(default=0)
  142. mails_bounced = models.IntegerField(default=0)
  143. date = models.DateField()
  144. class Meta:
  145. ordering = ['-date']
  146. unique_together = ('user_email', 'date')
  147. def __str__(self):
  148. return (
  149. 'Got {bounced} bounces out of {sent} '
  150. 'mails to {email} on {date}'.format(
  151. email=self.user_email,
  152. date=self.date,
  153. sent=self.mails_sent,
  154. bounced=self.mails_bounced)
  155. )