class Page(MPTTModel): title = models.CharField(max_length=200) parent = models.ForeignKey('self', related_name='children', blank=True, null=True, on_delete=models.CASCADE) template = Template( key='test', regions=[ Region(key='main', title='main region'), Region(key='sidebar', title='sidebar region', inherited=True), ], ) class Meta: verbose_name = 'page' verbose_name_plural = 'pages' def get_absolute_url(self): return reverse('page_detail', kwargs={'pk': self.pk}) @property def regions(self): return self.template.regions
class Page(models.Model): title = models.CharField(max_length=200) parent = models.ForeignKey("self", related_name="children", blank=True, null=True, on_delete=models.CASCADE) template = Template( key="test", regions=[ Region(key="main", title="main region"), Region(key="sidebar", title="sidebar region", inherited=True), ], ) class Meta: verbose_name = "page" verbose_name_plural = "pages" def get_absolute_url(self): return reverse("page_detail", kwargs={"pk": self.pk}) @property def regions(self): return self.template.regions
class Page( AbstractPage, AppsMixin, # For adding the articles app to pages through the CMS. TemplateMixin, # Two page templates, one with only a main # region and another with a sidebar as well. MenuMixin, # We have a main and a footer navigation (meta). LanguageMixin, # We're building a multilingual CMS. (Also, # feincms3.apps depends on LanguageMixin # currently.) RedirectMixin, # Allow redirecting pages to other pages and/or arbitrary # URLs. ): # TemplateMixin TEMPLATES = [ Template( key="standard", title=_("standard"), template_name="pages/standard.html", regions=(Region(key="main", title=_("Main")), ), ), Template( key="with-sidebar", title=_("with sidebar"), template_name="pages/with-sidebar.html", regions=( Region(key="main", title=_("Main")), Region(key="sidebar", title=_("Sidebar")), ), ), ] # MenuMixin MENUS = [("main", _("main")), ("footer", _("footer"))] # AppsMixin. We have two apps, one is for company PR, the other # for a more informal blog. # # NOTE! The app names (first element in the tuple) have to match the # article categories exactly for URL reversing and filtering articles by # app to work! (See app.articles.models.Article.CATEGORIES) APPLICATIONS = [ ("publications", _("publications"), { "urlconf": "testapp.articles_urls" }), ("blog", _("blog"), { "urlconf": "testapp.articles_urls" }), ( "stuff-with-required", "stuff-with-required", { "urlconf": "stuff-with-required", "required_fields": ("optional", "not_editable"), }, ), ] optional = models.IntegerField(blank=True, null=True) not_editable = models.IntegerField(blank=True, null=True, editable=False)
class Article(models.Model): created = models.DateTimeField(_("_created"), auto_now_add=True) updated = models.DateTimeField(_("_updated"), auto_now=True) headline = models.CharField(_("_headline"), max_length=200) teaser = models.TextField(_("_teaser")) slug = models.SlugField(_("_slug"), max_length=200) in_menu = models.BooleanField(_("_in_menu"), default=False) menu_order = models.IntegerField(_("_menu_order"), null=True, blank=True) tags = TaggableManager(blank=True) regions = [ Region(key="main", title="main region"), Region(key="sidebar", title="sidebar region", inherited=False), ] class Meta: unique_together = (("slug", "created"), ) ordering = ["-updated"] def get_absolute_url(self): if self.slug == "about": return "/" elif self.in_menu: return f"/{self.slug}" else: return f"/articles/{self.created.year}/{self.created.month}/{self.slug}" def get_unique_identifier(self): return f"{self.created.year}_{self.created.month}_{self.slug}" def __str__(self): return self.headline
class Page( AbstractPage, AppsMixin, # For adding the articles app to pages through the CMS. TemplateMixin, # Two page templates, one with only a main # region and another with a sidebar as well. MenuMixin, # We have a main and a footer navigation (meta). LanguageMixin, # We're building a multilingual CMS. (Also, # feincms3.apps depends on LanguageMixin # currently.) RedirectMixin, # Allow redirecting pages to other pages and/or arbitrary # URLs. ): # TemplateMixin TEMPLATES = [ Template( key='standard', title=_('standard'), template_name='pages/standard.html', regions=( Region(key='main', title=_('Main')), ), ), Template( key='with-sidebar', title=_('with sidebar'), template_name='pages/with-sidebar.html', regions=( Region(key='main', title=_('Main')), Region(key='sidebar', title=_('Sidebar')), ), ), ] # MenuMixin MENUS = [ ('main', _('main')), ('footer', _('footer')), ] # AppsMixin. We have two apps, one is for company PR, the other # for a more informal blog. # # NOTE! The app names (first element in the tuple) have to match the # article categories exactly for URL reversing and filtering articles by # app to work! (See app.articles.models.Article.CATEGORIES) APPLICATIONS = [ ('publications', _('publications'), { 'urlconf': 'testapp.articles_urls', }), ('blog', _('blog'), { 'urlconf': 'testapp.articles_urls', }), ] objects = PageManager()
class Article(models.Model): title = models.CharField(max_length=200) regions = [ Region(key="main", title="main region"), Region(key="sidebar", title="sidebar region"), ] def __str__(self): return self.title def get_absolute_url(self): return reverse("article_detail", kwargs={"pk": self.pk})
class Article(models.Model): title = models.CharField(max_length=200) regions = [ Region(key='main', title='main region'), Region(key='sidebar', title='sidebar region'), ] def __str__(self): return self.title def get_absolute_url(self): return reverse('article_detail', kwargs={'pk': self.pk})
class Page( AbstractPage, PageTypeMixin, # For adding the articles app to pages through the CMS. MenuMixin, # We have a main and a footer navigation (meta). LanguageMixin, # We're building a multilingual CMS. (Also, # feincms3.applications depends on LanguageMixin # currently.) ): TYPES = [ TemplateType( key="standard", title=_("standard"), template_name="pages/standard.html", regions=(Region(key="main", title=_("Main")), ), ), TemplateType( key="with-sidebar", title=_("with sidebar"), template_name="pages/with-sidebar.html", regions=( Region(key="main", title=_("Main")), Region(key="sidebar", title=_("Sidebar")), ), ), ApplicationType( key="publications", title=_("publications"), urlconf="app.articles.urls", regions=(Region(key="main", title=_("Main")), ), ), ApplicationType( key="blog", title=_("blog"), urlconf="app.articles.urls", regions=(Region(key="main", title=_("Main")), ), ), ] # MenuMixin MENUS = [ ("main", _("main")), ("footer", _("footer")), ]
def get_template_list(app_name, templates): return [ Template( key=template[0], title=_(template[0]).title(), template_name=f"{app_name}/{template[0]}.html", regions=[ Region(key=region, title=region.title(), inherited=True) for region in template[1] ] ) for template in templates ]
class Page(AbstractPage, PageTypeMixin, MenuMixin, LanguageMixin, RedirectMixin): # MenuMixin MENUS = [("main", _("main")), ("footer", _("footer"))] # PageTypeMixin. We have two templates and four apps. TYPES = [ TemplateType( key="standard", title=_("standard"), template_name="pages/standard.html", regions=(Region(key="main", title=_("Main")),), ), TemplateType( key="with-sidebar", title=_("with sidebar"), template_name="pages/with-sidebar.html", regions=( Region(key="main", title=_("Main")), Region(key="sidebar", title=_("Sidebar")), ), ), ApplicationType( key="publications", title=_("publications"), urlconf="testapp.articles_urls", ), ApplicationType( key="blog", title=_("blog"), urlconf="testapp.articles_urls", ), ApplicationType( key="stuff-with-required", title="stuff-with-required", urlconf="importable_module", required_fields=("optional", "not_editable"), ), ]
class Article(models.Model): regions = [Region(key="main", title=_("main"))]
class Page( AbstractPage, AppsMixin, # For adding the articles app to pages through the CMS. TemplateMixin, # Two page templates, one with only a main # region and another with a sidebar as well. MenuMixin, # We have a main and a footer navigation (meta). LanguageMixin, # We're building a multilingual CMS. (Also, # feincms3.apps depends on LanguageMixin # currently.) ): # TemplateMixin TEMPLATES = [ Template( key='standard', title=_('standard'), template_name='pages/standard.html', regions=( Region(key='main', title=_('Main'), content='contents.main'), Region(key='footer', title=_('Footer'), inherited=True, content='contents.footer'), ), ), Template( key='with-sidebar', title=_('with sidebar'), template_name='pages/with-sidebar.html', regions=( Region(key='main', title=_('Main'), content='contents.main'), Region(key='sidebar', title=_('Sidebar'), content='contents.sidebar'), Region(key='footer', title=_('Footer'), inherited=True, content='contents.footer'), ), ), ] # MenuMixin MENUS = [ ('main', _('main')), ('footer', _('footer')), ] def get_plugins(self): renderers = get_available_renderers() page = get_object_or_404(Page, pk=self.id) contents = contents_for_item(page, PagePlugins) data = [] for region in self.regions: new_data = { region.key: [ dict(renderers[plugin.__class__](plugin), type=plugin.__class__.__name__, lang=page.language_code, parent=page.slug) ] for plugin in eval(region.content) } data.append(new_data) return data # AppsMixin. We have two apps, one is for company PR, the other # for a more informal blog. # # NOTE! The app names (first element in the tuple) have to match the # article categories exactly for URL reversing and filtering articles by # app to work! (See app.articles.models.Article.CATEGORIES) APPLICATIONS = [ ('publications', _('publications'), { 'urlconf': 'app.articles.urls', }), ('blog', _('blog'), { 'urlconf': 'app.articles.urls', }), ]
class Location(MetaMixin, TranslationMixin): regions = [Region(key="images", title=_("images"))] name = models.CharField(max_length=200, verbose_name=_("name")) slug = models.SlugField(unique=True) street = models.CharField(max_length=200, verbose_name=_("street")) city = models.CharField(max_length=100, verbose_name=_("city")) zip_code = models.CharField(max_length=20, verbose_name=_("zip code")) country = models.CharField(max_length=200, verbose_name=_("country")) is_physical = models.BooleanField( verbose_name=_("is physical"), default=True, ) header_image = ImageField( _("header image"), formats={ "full": ["default", "darken", ("crop", (1920, 900))], "mobile": ["default", ("crop", (740, 600))], }, auto_add_fields=True, blank=True, null=True, ) section = models.ForeignKey(Section, models.SET_NULL, blank=True, null=True, verbose_name=_("section")) website = models.URLField(blank=True, verbose_name=_("website")) lng = models.FloatField(verbose_name=_("longitude"), default=0) lat = models.FloatField(verbose_name=_("latitude"), default=0) tags = TaggableManager(blank=True) def maps(self): return settings.MAPS_URL.format(location=self) def __str__(self): return self.name @property def title(self): return self.name @property def address(self): return f"{self.street}, {self.zip_code} {self.city}" class Meta: verbose_name = _("location") verbose_name_plural = _("locations") ordering = ["name"] def get_absolute_url(self): try: site = current_site() if not self.section or site == self.section.site: return reverse_app([f"{site.pk}-events"], "location-detail", kwargs={"slug": self.slug}) with set_current_site(self.section.site): return ("//" + self.section.site.host + reverse_app( [f"{self.section.site.id}-events"], "location-detail", urlconf=apps_urlconf(), kwargs={"slug": self.slug}, )) except NoReverseMatch: return "#"
class EmailMessage(models.Model): ORDER_CHOICES = (('created', 'Oldest first'), ('-created', 'Newest first'), ('?', 'Random')) regions = [ Region(key='body', title='Main Body'), # Region(key='split_test', title='Split Test Body', # inherited=False), ] name = models.CharField(max_length=255, unique=True, verbose_name='Email Message Name', help_text='A unique name for this email message.') enabled = models.BooleanField(default=False) note = models.TextField(max_length=255, null=True, blank=True, help_text="This is only seen by staff.") from_email = models.EmailField(null=True, blank=True, help_text='Set a custom from email.') from_email_name = models.CharField( max_length=150, null=True, blank=True, help_text="Set a name for a custom from email.") message_class = models.CharField(max_length=120, blank=True, default='default') slice = models.IntegerField( null=True, blank=True, help_text= 'Send to only a specific amount of subscribers. Useful for testing to a portion of the list. A value of 1000 would send to 1000 subscribers each time this EmailMessage is sent out.' ) subscriber_order = models.CharField( max_length=25, choices=ORDER_CHOICES, default='created', help_text='Send out to subscribers in this order.') send_after = models.DateTimeField(blank=True, null=True) disable_after_broadcast = models.BooleanField( default=False, help_text= "Useful with 'send after' broadcasts. After broadcast is initiated, disable this drip so it won't try to go out again." ) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) class Meta: app_label = 'squeezemail' def handler(self, *args, **kwargs): kwargs['email_message_model'] = self handler_class = class_for(SQUEEZE_EMAILMESSAGE_HANDLER) handler = handler_class(*args, **kwargs) return handler def __str__(self): return self.name @cached_property def lock_id(self): # example lock id: prefix_email_message_1 return str(SQUEEZE_PREFIX).encode('utf-8') + 'email_message_'.encode( 'utf-8') + str(self.id).encode('utf-8') def acquire_lock(self): return cache.add(self.lock_id, 'true', LOCK_EXPIRE) def release_lock(self): return cache.delete(self.lock_id) @cached_property def subject(self): return self.choose_split_test_subject.text @cached_property def get_split_test_subjects(self): return self.subjects.filter(enabled=True) @cached_property def split_subject_active(self): return self.get_split_test_subjects.count() > 1 @cached_property def choose_split_test_subject(self): # Return a subject object to be able to get the subject text and the subject id random_subject = self.subjects.filter(enabled=True).order_by('?')[0] return random_subject def get_split_test_body(self): pass def step_run(self, calling_step, subscription_qs): subscriber_qs = Subscriber.objects.filter( id__in=subscription_qs.values_list('subscriber_id', flat=True)) successfully_sent_subscriber_qs = self.handler( step=calling_step, queryset=subscriber_qs).step_run(calling_step, subscription_qs) # assert False, successfully_sent_subscriber_qs return successfully_sent_subscriber_qs def apply_queryset_rules(self, qs): """ First collect all filter/exclude kwargs and apply any annotations. Then apply all filters at once, and all excludes at once. """ clauses = {'filter': [], 'exclude': []} for rule in self.queryset_rules.all(): clause = clauses.get(rule.method_type, clauses['filter']) kwargs = rule.filter_kwargs(qs) clause.append(Q(**kwargs)) qs = rule.apply_any_annotation(qs) if clauses['exclude']: qs = qs.exclude(functools.reduce(operator.or_, clauses['exclude'])) qs = qs.filter(*clauses['filter']) return qs @cached_property def total_sent(self): return self.sent_email_messages.all().count() @cached_property def total_unique_opens(self): return Open.objects.filter( sent_email_message__email_message_id=self.pk).count() @cached_property def total_opens(self): return Open.objects.filter( sent_email_message__email_message_id=self.pk).aggregate( Sum('total'))['total__sum'] or 0 @cached_property def total_unique_clicks(self): return Click.objects.filter( sent_email_message__email_message_id=self.pk).count() @cached_property def total_clicks(self): return Click.objects.filter( sent_email_message__email_message_id=self.pk).aggregate( Sum('total'))['total__sum'] or 0 @cached_property def total_unsubscribes(self): return Unsubscribe.objects.filter( sent_email_message__email_message_id=self.pk).count() @cached_property def total_bounces(self): return Bounce.objects.filter( sent_email_message__email_message_id=self.pk).count() @cached_property def total_spammed(self): return Spam.objects.filter( sent_email_message__email_message_id=self.pk).count() def unique_open_rate(self): total_sent = self.total_sent total_opened = self.total_unique_opens if total_opened and total_sent > 0 and total_opened > 0: return "{0:.2f}%".format((total_opened / total_sent) * 100.0) return 0 def open_rate(self): total_sent = self.total_sent total_opened = self.total_opens if total_opened and total_sent > 0 and total_opened > 0: return "{0:.2f}%".format((total_opened / total_sent) * 100.0) return 0 def unique_click_rate(self): total_sent = self.total_sent total_clicked = self.total_unique_clicks if total_clicked and total_sent > 0 and total_clicked > 0: return "{0:.2f}%".format((total_clicked / total_sent) * 100.0) return 0 def click_rate(self): total_sent = self.total_sent total_clicked = self.total_clicks if total_clicked and total_sent > 0 and total_clicked > 0: return "{0:.2f}%".format((total_clicked / total_sent) * 100.0) return 0 def unique_click_to_open_rate(self): """ Click to open rate is the percentage of recipients who opened the email message and also clicked on any link in the email message. """ total_opened = self.total_opens total_clicked = self.total_unique_clicks if total_clicked and total_opened > 0 and total_clicked > 0: return "{0:.2f}%".format((total_clicked / total_opened) * 100.0) return 0 def click_to_open_rate(self): """ Click to open rate is the percentage of recipients who opened the email message and also clicked on any link in the email message. """ total_opened = self.total_opens total_clicked = self.total_clicks if total_clicked and total_opened > 0 and total_clicked > 0: return "{0:.2f}%".format((total_clicked / total_opened) * 100.0) return 0 def bounce_rate(self): total_sent = self.total_sent total_bounced = self.total_bounces if total_sent > 0 and total_bounced > 0: return "{0:.2f}%".format((total_bounced / total_sent) * 100.0) return 0 def unsubscribe_rate(self): total_sent = self.total_sent total_unsubscribed = self.total_unsubscribes if total_sent > 0 and total_unsubscribed > 0: return "{0:.2f}%".format((total_unsubscribed / total_sent) * 100.0) return 0 def spam_rate(self): total_sent = self.total_sent total_spammed = self.total_spammed if total_sent > 0 and total_spammed > 0: return "{0:.2f}%".format((total_spammed / total_sent) * 100.0) return 0 def disable(self): self.enabled = False return self.save()
class Event(models.Model): """Model representing a CompClub event.""" name = models.CharField(max_length=100) start_date = models.DateField( help_text= "Users without the view_unreleased_event permission cannot see the event until start date and the event is released." ) # noqa: E501 finish_date = models.DateField() owner = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True) slug = models.SlugField( default='event', unique=False, help_text= "The slug is the what appears in the URL. If you leave it as the default the slug will be autogenerated." ) # noqa: E501 description = models.TextField(null="", default="") display_image = models.ImageField( upload_to='uploads/%Y/%m/', max_length=150, null=True, blank=True, default="", help_text= "This field is optional. Please provide it if you are highlighting the event." ) # noqa: E501 highlighted_event = models.BooleanField( default=False, help_text="Display this event on the homepage") hidden_event = models.BooleanField( default=True, help_text= "Users cannot view the event (including on the events feed)", # noqa: E501 ) released = models.BooleanField( default=True, help_text= "Leave this checked if you want to automatically release the event on the start date (at midnight).", # noqa: E501 ) regions = [Region(key='main', title='main region')] class Meta: # noqa: D106 permissions = [ ("view_hidden_event", "Can view hidden events"), ("view_unreleased_event", "Can view unreleased events"), ] def __str__(self): """Return a string representation of an event.""" return self.name def clean(self): """Restrict highlighting to events with display images only.""" if self.highlighted_event and self.hidden_event: raise ValidationError( 'You can\'t highlight an event and hide it at the same time.') if self.highlighted_event and not self.display_image: raise ValidationError( 'You must provide a display image if highlighting the event.') def save(self, *args, **kwargs): """Override save to update slug.""" self.slug = slugify(self.name) if self.display_image and str(self.display_image.path) != str( self.display_image.file): self.display_image = compressors.compress_image(self.display_image) super(Event, self).save(*args, **kwargs)