List which fields are available when building tweet templates
authorMagnus Hagander <magnus@hagander.net>
Wed, 21 Aug 2024 11:47:55 +0000 (13:47 +0200)
committerMagnus Hagander <magnus@hagander.net>
Wed, 21 Aug 2024 11:56:29 +0000 (13:56 +0200)
Both for sponsor benefits and for campaign creation, list which fields
are available to use in the Jinja templates, to make it easier to figure
out how to use them.

postgresqleu/confreg/campaigns.py
postgresqleu/confsponsor/backendforms.py
postgresqleu/confsponsor/models.py

index 456aee5602f0d125c0d249dfca87edf8e212864c..0230ddcc5595c4a61423b7f313c20882488a3c52 100644 (file)
@@ -4,10 +4,11 @@ from django.http import Http404, HttpResponse
 from django.utils.dateparse import parse_datetime, parse_duration
 from django.utils import timezone
 
-from postgresqleu.util.widgets import MonospaceTextarea
-from postgresqleu.confreg.models import ConferenceSession, Track
+from postgresqleu.util.widgets import MonospaceTextarea, SimpleTreeviewWidget
+from postgresqleu.confreg.models import Conference, ConferenceSession, Track
 from postgresqleu.confreg.models import MessagingProvider
 from postgresqleu.confreg.twitter import post_conference_social, render_multiprovider_tweet
+from postgresqleu.confreg.jinjafunc import get_all_available_attributes
 from postgresqleu.confsponsor.models import Sponsor, SponsorshipLevel
 from postgresqleu.util.messaging import get_messaging, get_messaging_class
 from postgresqleu.util.messaging.short import get_shortened_post_length
@@ -54,16 +55,19 @@ class BaseCampaignForm(forms.Form):
     content_template = forms.CharField(max_length=2000,
                                        widget=MonospaceTextarea,
                                        required=True)
+    available_fields = forms.CharField(required=False, help_text="These fields are available as {{field}} in the template")
     dynamic_preview_fields = ['content_template', ]
 
     confirm = forms.BooleanField(help_text="Confirm that you want to generate all the tweets for this campaign at this time", required=False)
 
     def __init__(self, conference, *args, **kwargs):
         self.conference = conference
-        self.field_order = ['starttime', 'timebetween', 'timerandom', 'content_template'] + self.custom_fields + ['confirm', ]
+        self.field_order = ['starttime', 'timebetween', 'timerandom', 'content_template', 'available_fields', ] + self.custom_fields + ['confirm', ]
 
         super(BaseCampaignForm, self).__init__(*args, *kwargs)
 
+        self.fields['available_fields'].widget = SimpleTreeviewWidget(treedata=self.get_contextrefs())
+
         if not all([self.data.get(f) for f in ['starttime', 'timebetween', 'timerandom', 'content_template'] + self.custom_fields]):
             del self.fields['confirm']
         else:
@@ -80,6 +84,9 @@ class BaseCampaignForm(forms.Form):
             else:
                 self.fields['confirm'].help_text = "Campaign matches no entries. Try again."
 
+    def get_contextrefs(self):
+        return {}
+
     def clean_confirm(self):
         if not self.cleaned_data['confirm']:
             if self.get_queryset().count == 0:
@@ -119,6 +126,13 @@ class ApprovedSessionsCampaignForm(BaseCampaignForm):
             'session': session,
         })
 
+    @classmethod
+    def get_contextrefs(cls):
+        return {
+            'conference': dict(get_all_available_attributes(Conference)),
+            'session': dict(get_all_available_attributes(ConferenceSession)),
+        }
+
     def get_queryset(self):
         return ConferenceSession.objects.filter(conference=self.conference, status=1, cross_schedule=False, track__in=self.data.getlist('tracks'))
 
@@ -139,6 +153,13 @@ class SponsorsCampaignForm(BaseCampaignForm):
             'sponsor': sponsor,
         })
 
+    @classmethod
+    def get_contextrefs(cls):
+        return {
+            'conference': dict(get_all_available_attributes(Conference)),
+            'sponsor': dict(get_all_available_attributes(Sponsor)),
+        }
+
     def get_queryset(self):
         return Sponsor.objects.filter(conference=self.conference, confirmed=True, level__in=self.data.getlist('levels'))
 
index 731a65fa4caafa859dc76c4b2737930e5276ea6c..d85656a1990e9a5a8aca2fe4b79df1e01c83b3ee 100644 (file)
@@ -7,14 +7,16 @@ from collections import OrderedDict
 import json
 
 from postgresqleu.util.db import exec_to_scalar
-from postgresqleu.util.widgets import StaticTextWidget
+from postgresqleu.util.widgets import StaticTextWidget, SimpleTreeviewWidget
 from postgresqleu.util.widgets import MonospaceTextarea
 from postgresqleu.util.backendforms import BackendForm, BackendBeforeNewForm
 from postgresqleu.util.backendlookups import GeneralAccountLookup
 from postgresqleu.confreg.jinjafunc import JinjaTemplateValidator, filter_social
+from postgresqleu.confreg.jinjafunc import get_all_available_attributes
 from postgresqleu.confreg.twitter import get_all_conference_social_media
 from postgresqleu.confreg.twitter import render_multiprovider_tweet
 
+from postgresqleu.confreg.models import Conference
 from .models import Sponsor
 from .models import SponsorshipLevel, SponsorshipContract, SponsorshipBenefit
 from .models import ShipmentAddress
@@ -107,12 +109,13 @@ class BackendSponsorshipLevelBenefitForm(BackendForm):
     markdown_fields = ['benefitdescription', 'claimprompt', ]
     dynamic_preview_fields = ['tweet_template']
     form_before_new = BackendSponsorshipNewBenefitForm
-    readonly_fields = ['benefit_class_name', ]
+    readonly_fields = ['benefit_class_name', 'available_fields', ]
     exclude_date_validators = ['deadline', ]
 
     class_param_fields = []  # Overridden in subclass!
 
     benefit_class_name = django.forms.CharField(required=False)
+    available_fields = django.forms.CharField(required=False)
 
     @property
     def fieldsets(self):
@@ -125,7 +128,7 @@ class BackendSponsorshipLevelBenefitForm(BackendForm):
         return [
             {'id': 'base', 'legend': 'Base', 'fields': basefields},
             {'id': 'download', 'legend': 'Token download data', 'fields': ['overview_name', 'overview_value', 'include_in_data']},
-            {'id': 'marketing', 'legend': 'Marketing', 'fields': ['tweet_template', ]},
+            {'id': 'marketing', 'legend': 'Marketing', 'fields': ['tweet_template', 'available_fields', ]},
             {'id': 'params', 'legend': 'Parameters', 'fields': self.class_param_fields},
         ]
 
@@ -138,7 +141,7 @@ class BackendSponsorshipLevelBenefitForm(BackendForm):
     class Meta:
         model = SponsorshipBenefit
         fields = ['benefitname', 'benefitdescription', 'sortkey', 'maxclaims',
-                  'claimprompt', 'deadline', 'tweet_template', 'benefit_class_name', 'autoconfirm',
+                  'claimprompt', 'deadline', 'tweet_template', 'available_fields', 'benefit_class_name', 'autoconfirm',
                   'overview_name', 'overview_value', 'include_in_data', ]
 
     _can_multiclaim = None
@@ -193,6 +196,8 @@ class BackendSponsorshipLevelBenefitForm(BackendForm):
         ]
         self.fields['tweet_template'].widget = MonospaceTextarea()
 
+        self.fields['available_fields'].widget = SimpleTreeviewWidget(treedata=self.get_contextrefs())
+
         if not self.can_multiclaim:
             del self.fields['maxclaims']
             self.update_protected_fields()
@@ -221,6 +226,15 @@ class BackendSponsorshipLevelBenefitForm(BackendForm):
                     return p
             return ''
 
+    @classmethod
+    def get_contextrefs(self):
+        return {
+            'benefit': dict(get_all_available_attributes(SponsorshipBenefit)),
+            'level': dict(get_all_available_attributes(SponsorshipLevel)),
+            'conference': dict(get_all_available_attributes(Conference)),
+            'sponsor': dict(get_all_available_attributes(Sponsor)),
+        }
+
 
 class BackendSponsorshipLevelBenefitManager(object):
     title = 'Benefits'
index 823cc61639d73ab429deb4da64cc9bd99a461380..510071b3a9a349b506f1e4efae5b0d71080659ce 100644 (file)
@@ -108,6 +108,11 @@ class SponsorshipBenefit(models.Model):
     include_in_data = models.BooleanField(null=False, default=True, verbose_name="Include in data",
                                           help_text='Include information about this benefit in sponsorship data from tokens')
 
+    _safe_attributes = [
+        'benefitname', 'sortkey', 'benefitdescription', 'claimprompt', 'maxclaims', 'deadline',
+        'autoconfirm', 'benefit_class', 'class_parameters',
+    ]
+
     def __str__(self):
         return self.benefitname