class AdministrativeArea(models.Model): """ Administrative Area level 1 for a country. For the US, this would be the states. """ status = models.StatusField( verbose_name=_('Area is active'), ) country = models.ForeignKey( Country, related_name='administrative_areas', ) name = models.CharField( max_length=60, db_index=True, verbose_name=_('Admin Area name'), ) abbrev = models.CharField( max_length=3, blank=True, null=True, verbose_name=_('Postal Abbreviation'), ) def __str__(self): return self.name class Meta: verbose_name = _('Administrative Area') verbose_name_plural = _('Administrative Areas') ordering = ('name',)
class CountryTranslation(models.TranslationModel): country = models.ForeignKey( Country, related_name='translations', ) name = models.CharField( max_length=128, db_index=True, verbose_name=_('Official name (CAPS)'), ) printable_name = models.CharField( max_length=128, db_index=True, verbose_name=_('Country name'), ) class Meta: verbose_name = _('Country Name Translation') verbose_name_plural = _('Country Name Translations') unique_together = (('language', 'country',),) def __str__(self): return '%(translation)s (%(country)s)' % { 'country': self.country.printable_name, 'translation': self.printable_name, }
class TermsOfServiceTranslation(models.TranslationModel): parent = models.ForeignKey( TermsOfService, db_index=True, on_delete=models.CASCADE, related_name='translations', verbose_name=_('TermsOfService'), ) label = models.TextField( blank=True, default='', verbose_name=_('label'), ) title = models.CharField( max_length=255, blank=True, default='', verbose_name=_('legal title'), help_text=_('legal tos title'), ) text = models.TextField( blank=True, default='', verbose_name=_('legal text'), help_text=_('legal tos text'), ) human_title = models.CharField( max_length=255, blank=True, default='', verbose_name=_('human title'), help_text=_('human readable tos title'), ) human_text = models.TextField( blank=True, default='', verbose_name=_('human text'), help_text=_('human readable tos text'), ) changelog = models.TextField( blank=True, default='', verbose_name=_('changelog'), help_text=_('difference(s) from previous version'), ) class Meta: unique_together = (('language', 'parent'), ('human_title', 'human_text')) verbose_name = _('TermsOfService Translation') verbose_name_plural = _('TermsOfService Translations') def __str__(self): return self.title
class Option(models.TimestampModel, models.I18NModel, models.OrderedModel): key = models.CharField( max_length=255, db_index=True, verbose_name=_('key'), ) required = models.BooleanField( default=False, verbose_name=_('required'), ) default = models.BooleanField( default=False, verbose_name=_('default value'), ) label = models.TextField(verbose_name=_('label'), ) description = models.TextField( null=True, blank=True, verbose_name=_('description'), ) error_message = models.TextField( blank=True, default='', verbose_name=_('error message'), ) class Meta: ordering = ('ordering', ) unique_together = ('ordering', 'key') verbose_name = _('option') verbose_name_plural = _('options') def __str__(self): return self.key
class Email(models.Model): service = models.ForeignKey( Service, related_name="emails", verbose_name=_("service"), ) name = models.CharField( max_length=255, blank=True, null=True, verbose_name=_("name"), ) email = models.EmailField( max_length=255, verbose_name=_("email"), ) class Meta: unique_together = [("service", "email")] verbose_name = _("Service email") verbose_name_plural = _("Service email") def __str__(self): return _("Email for {name}: {email}").format( name=self.service.name, email=self.email, )
class Country(models.I18NModel): """ International Organization for Standardization (ISO) 3166-1 Country list """ status = models.StatusField( verbose_name=_('Country is active'), ) name = models.CharField( max_length=128, db_index=True, verbose_name=_('Official name (CAPS)'), ) printable_name = models.CharField( max_length=128, db_index=True, verbose_name=_('Country name'), ) iso2_code = models.CharField( max_length=2, unique=True, db_index=True, verbose_name=_('ISO alpha-2'), ) iso3_code = models.CharField( max_length=3, unique=True, verbose_name=_('ISO alpha-3'), ) numcode = models.PositiveSmallIntegerField( null=True, blank=True, verbose_name=_('ISO numeric'), ) continent = models.CharField( choices=CONTINENTS, max_length=13, verbose_name=_('Continent'), ) admin_area = models.CharField( choices=AREAS, max_length=20, blank=True, null=True, db_index=True, verbose_name=_('Administrative Area'), ) class Meta: verbose_name = _('Country') verbose_name_plural = _('Countries') ordering = ('iso2_code', 'name',) def __str__(self): return self.printable_name
class Campaign(models.TimestampModel): name = models.CharField( max_length=255, unique=True, verbose_name=_("Name"), ) description = models.TextField( blank=True, verbose_name=_("Description"), ) class Meta: ordering = ["name"] verbose_name = _("Campaign") verbose_name_plural = _("Campaigns") def __str__(self): return self.name
class Service(models.StatusModel, models.OrderedModel): objects = ServiceManager() name = models.CharField( unique=True, max_length=255, ) slug = models.SlugField( unique=True, max_length=255, editable=False, verbose_name=_("slug"), help_text= ('A "slug" is a unique URL-friendly title for the object automatically generated from the "name" field.' ), ) default = models.BooleanField( default=False, verbose_name=_("default"), help_text=_("Is the default one?"), ) class Meta: ordering = ["name"] verbose_name = _("Service") verbose_name_plural = _("Services") def __str__(self): return self.name def save(self, *args, **kwargs): self.slug = slugify(self.name) super(Service, self).save(*args, **kwargs) if self.default: for c in self._default_manager.exclude(pk=self.id): c.default = False c.save(*args, **kwargs) try: c = self._default_manager.get(default=True) except models.ObjectDoesNotExist: self.default = True super(Service, self).save(*args, **kwargs)
class TermsOfService(models.TimestampModel, models.I18NModel): DRAFT = 'draft' PUBLISHED = 'published' REVIEW = 'review' STATUS_CHOICES = ( (DRAFT, _('Draft')), (REVIEW, _('Review')), (PUBLISHED, _('Published')), ) objects = TermsOfServiceManager() status = models.StatusField( choices=STATUS_CHOICES, default=DRAFT, help_text=_('If should be displayed or not.'), ) version = models.CharField( max_length=15, unique=True, db_index=True, verbose_name=_('version'), ) date_begin = models.DateTimeField( blank=True, null=True, verbose_name=_('date begin'), help_text=_('When TOS begins to be effective.'), ) date_end = models.DateTimeField( blank=True, null=True, verbose_name=_('Date end'), help_text=_('When TOS ends to be effective.'), ) label = models.TextField( blank=True, default='', verbose_name=_('label'), ) title = models.CharField( max_length=255, blank=True, default='', verbose_name=_('Title'), ) text = models.TextField( blank=True, default='', verbose_name=_('Default text'), ) human_title = models.CharField( max_length=255, blank=True, default='', verbose_name=_('human title'), help_text=_('human readable tos title'), ) human_text = models.TextField( blank=True, default='', verbose_name=_('human text'), help_text=_('human readable tos text'), ) changelog = models.TextField( blank=True, default='', verbose_name=_('changelog'), help_text=_('difference(s) from previous version'), ) options = models.ManyToManyField( Option, blank=True, verbose_name=_('options'), ) class Meta: ordering = ("-date_begin", "-version") verbose_name = _('TermsOfService') verbose_name_plural = _('TermsOfService') def __str__(self): return 'Terms of Service %(version)s %(from)s [%(status)s]' % { 'from': self.date_begin, 'version': self.version, 'status': self.get_status_display(), } @property def active(self): current = TermsOfService.objects.current return self.id == current.id def get_absolute_url(self): return reverse('tos-detail', kewarg={'slug': self.translate().slug}) absolute_url = property(get_absolute_url) def get_preview_url(self): return reverse('tos-preview', kwargs={ 'slug': self.translate().slug, 'token': self.uuid }) preview_url = property(get_preview_url) def save(self, *args, **kwargs): now = timezone.now() if not self.date_begin and self.status == TermsOfService.PUBLISHED: self.date_begin = now super().save(*args, **kwargs) def publish(self, *args, **kwargs): self.status = TermsOfService.PUBLISHED self.save(*args, **kwargs) def unpublish(self, *args, **kwargs): self.status = TermsOfService.DRAFT self.save(*args, **kwargs) @property def prev(self): try: tos = TermsOfService.objects.published()[1] except IndexError: raise TermsOfService.DoesNotExist() return tos
class CommentModel(models.TimestampModel): """ A user comment about some object. """ objects = CommentManager() site = models.ForeignKey( Site, default=get_current_site, on_delete=models.CASCADE, related_name="comments", verbose_name=_("site"), ) parent = models.ForeignKey( "self", blank=True, null=True, on_delete=models.SET_NULL, related_name="children", verbose_name=_("parent comment"), ) user = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL, related_name="%(app_label)s_%(class)s_comments", verbose_name=_("user"), ) user_name = models.CharField(max_length=255, blank=True, verbose_name=_("user's name")) user_email = models.EmailField(max_length=255, blank=True, verbose_name=_("user's email address")) user_url = models.URLField(blank=True, verbose_name=_("user's URL")) comment = models.TextField(max_length=settings.MAX_LENGTH, verbose_name=_("comment")) notify_by_email = models.BooleanField(default=True, verbose_name=_("notify by email for updates")) ip_address = models.GenericIPAddressField(blank=True, null=True, verbose_name=_("IP address")) is_public = models.BooleanField( default=True, help_text=_("Uncheck this box to make the comment effectively disappear from the site."), verbose_name=_("is public"), ) is_removed = models.BooleanField( default=False, help_text=_( "Check this box if the comment is inappropriate. A " '"This comment has been removed" message will be displayed instead.' ), verbose_name=_("is removed"), ) class Meta: abstract = True ordering = ["created_at"] permissions = [("can_moderate", "Can moderate comments")] verbose_name = _("comment") verbose_name_plural = _("comments") def __str__(self): return "{name}: {comment}...".format(name=self.name, comment=self.comment[:50]) @property def name(self): return self.userinfo["name"] @name.setter def name(self, val): if self.user_id: raise AttributeError(_("This comment was posted by an authenticated user and thus the name is read-only.")) self.user_name = val @property def email(self): return self.userinfo["email"] @email.setter def email(self, val): if self.user_id: raise AttributeError(_("This comment was posted by an authenticated user and thus the email is read-only.")) self.user_email = val @property def url(self): return self.userinfo["url"] @url.setter def url(self, val): self.user_url = val @property def userinfo(self): if not hasattr(self, "_userinfo"): userinfo = {"name": self.user_name, "email": self.user_email, "url": self.user_url} if self.user_id: u = self.user if u.email: userinfo["email"] = u.email # If the user has a full name, use that for the user name. # However, a given user_name overrides the raw user.username, # so only use that if this comment has no associated name. if u.get_full_name(): userinfo["name"] = self.user.get_full_name() elif not self.user_name: userinfo["name"] = u.get_username() self._userinfo = userinfo return self._userinfo
class Coupon(models.TimestampModel): Error = exceptions.CouponError ExpiredError = exceptions.CouponExpiredError IsUsableError = exceptions.CouponIsUsableError objects = CouponManager() value = models.IntegerField( verbose_name=_("Value"), help_text=_("Arbitrary coupon value"), ) code = models.CharField( max_length=30, unique=True, blank=True, verbose_name=_("Code"), help_text=_("Leaving this field empty will generate a random code."), ) type = models.CharField( choices=COUPON_TYPES, max_length=20, verbose_name=_("Type"), ) action = models.CharField( choices=ACTION_TYPES, max_length=20, blank=True, default=DEFAULT_ACTION_TYPE, verbose_name=_("Action"), ) user_limit = models.PositiveIntegerField( default=1, verbose_name=_("User limit"), ) valid_from = models.DateTimeField( blank=True, null=True, verbose_name=_("Valid from"), help_text=_("Coupons are valid from this date"), ) valid_until = models.DateTimeField( blank=True, null=True, verbose_name=_("Valid until"), help_text=_("Coupons expire at this date"), ) campaign = models.ForeignKey( Campaign, blank=True, null=True, on_delete=models.CASCADE, related_name="coupons", verbose_name=_("Campaign"), ) class Meta: ordering = ["created_at"] verbose_name = _("Coupon") verbose_name_plural = _("Coupons") def __str__(self): return self.code def save(self, *args, **kwargs): if not self.code: self.code = Coupon.generate_code() super().save(*args, **kwargs) def expired(self): return self.is_expired @property def is_expired(self): return self.valid_until is not None and self.valid_until < timezone.now() @property def is_redeemed(self): """ Returns true is a coupon is redeemed (completely for all users) otherwise returns false. """ return self.users.filter( redeemed_at__isnull=False ).count() >= self.user_limit and self.user_limit is not 0 @property def redeemed_at(self): try: return self.users.filter(redeemed_at__isnull=False).order_by('redeemed_at').last().redeemed_at except self.users.through.DoesNotExist: return None @classmethod def generate_code(cls, prefix="", segmented=SEGMENTED_CODES, code_chars=CODE_CHARS, code_length=CODE_LENGTH): code = "".join(random.choice(code_chars) for i in range(code_length)) if segmented: code = SEGMENT_SEPARATOR.join([code[i:i + SEGMENT_LENGTH] for i in range(0, len(code), SEGMENT_LENGTH)]) return prefix + code else: return prefix + code @property def is_usable(self): user_limit = self.user_limit is_usable = -1 < CouponUser.objects.filter(coupon=self).count() < user_limit if is_usable: is_usable = self.do_is_usable_pipeline() return is_usable @transaction.atomic def redeem(self, user=None, source=None, **kwargs): if not self.is_usable: raise Coupon.IsUsableError() coupon_user = CouponUser(coupon=self, user=user) coupon_user.redeemed_at = timezone.now() if source is not None: coupon_user.source_type = models.ContentType.objects.get_for_model(source) coupon_user.source_id = source.pk coupon_user.save() self.do_redeem_pipeline(coupon_user=coupon_user, user=user, source=source, **kwargs) return coupon_user def do_is_usable_pipeline(self, **kwargs): coupon = self for name in getattr(settings, "COUPONS_IS_USABLE_PIPELINE", []): pipeline = import_string(name) coupon, is_usable = pipeline(coupon=coupon, **kwargs) if not is_usable: return False return True def do_redeem_pipeline(self, **kwargs): coupon = self for name in getattr(settings, "COUPONS_REDEEM_PIPELINE", []): pipeline = import_string(name) coupon = pipeline(coupon=coupon, **kwargs)
class Log(models.Model): NOTSET = "notset" DEBUG = "debug" INFO = "info" WARNING = "warning" ERROR = "error" CRITICAL = "critical" objects = LogManager() user = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, verbose_name=_("user"), ) timestamp = models.CreationDateTimeField( null=True, blank=True, verbose_name=_("timestamp"), ) level = models.CharField( max_length=255, default=NOTSET, choices=( (NOTSET, _("Not set")), (DEBUG, _("Debug")), (INFO, _("Info")), (WARNING, _("Warning")), (ERROR, _("Error")), (CRITICAL, _("Critical")), ), verbose_name=_("level"), ) content_type = models.ForeignKey( ContentType, blank=True, null=True, verbose_name=_("content type"), ) object_id = models.TextField( default="", blank=True, #null=True, verbose_name=_("object id"), ) object_repr = models.CharField( max_length=255, default="", blank=True, verbose_name=_("object repr"), ) message = models.TextField( default="", blank=True, verbose_name=_("message"), ) realm = models.TextField( blank=True, null=True, verbose_name=_("realm"), help_text=_("realm"), ) class Meta: ordering = ["-timestamp"] verbose_name = _("log") verbose_name_plural = _("logs") def __str__(self): msg = ugettext("{timestamp} {level} {message}") return msg.format( timestamp=self.timestamp, level=self.level, message=self.message[:255], )