class TestFieldRowPanelWithChooser(TestCase): def setUp(self): self.request = RequestFactory().get('/') user = AnonymousUser() # technically, Anonymous users cannot access the admin self.request.user = user self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', date_from=date(2014, 7, 19), date_to=date(2014, 7, 21)) self.dates_panel = FieldRowPanel([ FieldPanel('date_from'), ImageChooserPanel('feed_image'), ]).bind_to(model=EventPage, request=self.request) def test_render_as_object(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-22'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to( instance=self.event, form=form, ) result = field_panel.render_as_object() # check that the populated form field is included self.assertIn('value="2014-07-20"', result) # there should be no errors on this field self.assertNotIn('<p class="error-message">', result)
def setUp(self): self.request = RequestFactory().get('/') user = AnonymousUser() # technically, Anonymous users cannot access the admin self.request.user = user self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', date_from=date(2014, 7, 19), date_to=date(2014, 7, 21)) self.dates_panel = FieldRowPanel([ FieldPanel('date_from'), ImageChooserPanel('feed_image'), ]).bind_to(model=EventPage, request=self.request)
class _RUBIONUserModelDefinition(MoveMixin, UserGeneratedPage2): class Meta: abstract = True # This class is just to get more readable code # named dosemeters NOT_YET_DECIDED = '0' NO_DOSEMETER = '1' OFFICIAL_DOSEMETER = '2' ELECTRONIC_DOSEMETER = '3' EXTERNAL_DOSEMETER = '4' DOSEMETER_CHOICES = ( (NOT_YET_DECIDED, _('Not yet decided')), (NO_DOSEMETER, _('No dosemeter')), (OFFICIAL_DOSEMETER, _('Official dosemeter')), (ELECTRONIC_DOSEMETER, _('Electronic dosemeter')), (EXTERNAL_DOSEMETER, _('External dosemeter')), ) DOSEMETER_SHORT_NAMES = { NOT_YET_DECIDED: _('not decided'), NO_DOSEMETER: _('not required'), OFFICIAL_DOSEMETER: _('official'), ELECTRONIC_DOSEMETER: _('electronic'), EXTERNAL_DOSEMETER: _('external'), } FIELDS_TO_SYNC_WITH_STAFF = ['key_number', 'dosemeter'] # --- Fields # This is a bit annoying, since it repeats the definitions of the # normal wagtail user fields. These fields might be not necessary, # but I introduced them for some reasons (maybe for course users?) name_db = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('name')) name_db.context_description = 'Nachname' first_name_db = models.CharField(max_length=32, blank=True, null=True, verbose_name=_('first name')) first_name_db.context_description = 'Vorname' email_db = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('email address')) email_db.context_description = 'E-Mail-Adresse' # --- Fields for additional personal data # I don't like the question in the form "What is your gender?" MALE = 'm' FEMALE = 'f' SEXES = ( (MALE, _('male')), (FEMALE, _('female')), ) sex = models.CharField( max_length=1, choices=SEXES, blank=True, null=True, verbose_name=_('What is your gender?'), help_text= _('We ask this question to correctly address you in emails and letters send to you.' )) sex.context_description = 'Geschlecht' BSC = 'b' MSC = 'm' PHD = 'd' MD = 'n' PROF = 'p' NON = '' ACADEMIC_TITLES = ( (NON, _('None')), (BSC, _('BSc')), (MSC, _('MSc')), (PHD, _('PhD')), (MD, _('MD')), (PROF, _('Prof.')), ) academic_title = models.CharField(max_length=1, choices=ACADEMIC_TITLES, blank=True, null=True, verbose_name=_('academic title')) academic_title.context_description = 'Akademischer Titel' phone = models.CharField(max_length=24, verbose_name=_('Phone number'), blank=True, null=True) phone.context_description = 'Telefonnummer' linked_user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, verbose_name=_('linked website user'), on_delete=models.SET_NULL) preferred_language = models.CharField( max_length=2, choices=settings.LANGUAGES, null=True, blank=True, verbose_name=_('Preferred language for communication')) preferred_language.context_description = 'Bevorzugte Sprache' # --- Fields required by radiation safety date_of_birth = models.DateField(null=True, blank=True, verbose_name=_('Date of birth'), help_text=_('Format: YYYY-MM-DD')) date_of_birth.context_description = 'Geburtsdatum' place_of_birth = models.CharField( max_length=256, null=True, blank=True, verbose_name=_('Place of birth'), help_text=_('Add the country, if not Germany')) place_of_birth.context_description = 'Geburtsort' previous_names = models.CharField( max_length=512, null=True, blank=True, verbose_name=_('Previous names.'), help_text=_( 'Please list your previous name (in case of marriage, for example)' )) previous_names.context_description = 'Vorheriger Name' previous_exposure = models.BooleanField( default=False, verbose_name=_('Previous exposure'), help_text= _('Has there been work-related previous exposure to ionizing radiation?' )) previous_exposure.context_description = 'Vorherige Expositionen' # --- Fields for RUBION-internal use is_validated = models.BooleanField(default=False, verbose_name=_('is the user validated')) is_validated.context_description = 'Status der Validierung' has_agreed = models.BooleanField(default=False, verbose_name=_('has agreed')) has_agreed.context_description = 'Nutzungsbedingungen zugestimmt?' is_rub = models.BooleanField(default=False, verbose_name=_('is a member of the RUB')) is_rub.context_description = 'RUB-Mitglied?' # Safety-instructions are not yet implemented # last_safety_instructions = models.DateField( # null = True, # blank = True # ) needs_safety_instructions = ParentalManyToManyField( 'userdata.SafetyInstructionsSnippet', verbose_name=_('user needs safety instruction'), help_text=_('Which safety instructions are required for the user?'), blank=True) key_number = models.CharField(max_length=64, blank=True, default='', verbose_name=_('Key number')) key_number.context_description = 'Schlüsselnummer' labcoat_size = models.CharField(max_length=6, blank=True, default='', choices=( ('S', 'S'), ('M', 'M'), ('L', 'L'), ('XL', 'XL'), ('2XL', '2XL'), ('3XL', '3XL'), ('4XL', '4XL'), ('5XL', '5XL'), ), verbose_name=_('lab coat size'), help_text=_('The size of your lab coat.')) labcoat_size.context_description = 'Kittelgröße' overshoe_size = models.CharField( max_length=6, blank=True, default='', choices=( ('2', 'S-M'), ('3', 'L-XL'), ('4', 'XXL or above'), ), verbose_name=_('shoe size'), help_text=_('The size your protecting shoes should have.')) overshoe_size.context_description = 'Größe der Überschuhe' entrance = models.CharField( max_length=6, blank=True, default='', choices=( ('0', 'NB'), ('1', 'NC'), ('2', 'ND'), ('3', 'NI 06'), ), verbose_name=_('Preferred entrance in the lab.')) entrance.context_description = 'Eingang ins Labor' initially_seen = ParentalManyToManyField( 'userdata.StaffUser', # default = False, verbose_name=_('User seen by RUBION staff members'), blank=True) # --- Some simple permissions is_leader = models.BooleanField(default=False, verbose_name=_('leader of the group')) is_leader.context_description = 'Gruppenleiter?' # Can be set by the group leader may_create_projects = models.BooleanField( default=False, verbose_name=_('may create projects')) may_create_projects.context_description = 'Darf Projekte anlegen?' may_add_members = models.BooleanField(default=False, verbose_name=_('May add members')) may_add_members.context_description = 'Darf Mitglieder hinzufügen?' needs_key = models.BooleanField( default=False, verbose_name=_('Does this user permanently need a key?')) needs_key.context_description = 'Benötigt einen Schlüssel?' dosemeter = models.CharField(max_length=1, choices=DOSEMETER_CHOICES, default=NOT_YET_DECIDED, verbose_name=_('Type of dosemeter')) dosemeter.context_description = 'Benötigtes Dosimeter' admin_fields = [ 'key_number', 'is_group_leader', 'linked_user', 'title', 'title_de', 'is_validated', 'has_agreed', 'key_number', 'last_safety_instructions', 'needs_safety_instructions', 'is_rub', 'dosemeter' ] group_head_fields = ['needs_key', 'may_add_members', 'may_create_projects'] personal_fields = ['academic_title', 'phone', 'sex'] # Admin content panels. # @TODO might require some clean-up content_panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('name_db'), FieldPanel('first_name_db'), ]), FieldPanel('email_db'), FieldPanel('preferred_language'), FieldPanel('academic_title'), FieldPanel('sex'), ], heading=_('personal information')), MultiFieldPanel( [ FieldRowPanel([ FieldPanel('date_of_birth'), FieldPanel('place_of_birth') ]), FieldRowPanel([ FieldPanel('previous_names'), FieldPanel('previous_exposure') ]), ], heading=_('Additional personal data for radiation safety')), ] settings_panel = [ MultiFieldPanel([ FieldPanel('linked_user'), FieldRowPanel([ FieldPanel('has_agreed'), FieldPanel('is_validated'), ]), FieldRowPanel([ FieldPanel('is_rub'), FieldPanel('is_leader'), ]), ], heading=_('internal information')), MultiFieldPanel([ FieldRowPanel([ FieldPanel('may_create_projects'), FieldPanel('may_add_members'), ]) ], heading=_('user permissions')), ] rubion_panel = [ MultiFieldPanel([ FieldPanel('dosemeter'), FieldRowPanel([ FieldPanel('labcoat_size'), FieldPanel('overshoe_size'), ]), FieldPanel('entrance'), ], heading=_('Safety')), MultiFieldPanel([ FieldRowPanel([ FieldPanel('needs_key', classname="col4"), FieldPanel('key_number', classname="col8"), ]) ], heading=_('Key')), MultiFieldPanel([ InlinePanel('may_book', label=_('User may book')), ], heading='User permissions'), ] comment_panel = [FieldPanel('internal_rubion_comment')] si_panel = [ FieldPanel('needs_safety_instructions', widget=forms.CheckboxSelectMultiple), InlinePanel('rubion_user_si', panels=[ FieldRowPanel( [FieldPanel('instruction'), FieldPanel('date')]) ]) ] edit_handler = TabbedInterface([ ObjectList(rubion_panel, heading=_('Settings set by RUBION')), ObjectList(si_panel, heading=_('Safety instructions')), ObjectList(comment_panel, heading=_('internal comments')), ObjectList(settings_panel, heading=_('internal settings')), ObjectList(content_panels, heading=_('User Data')), ])
class Event(models.Model): FREE = 'FREE' PAID = 'PAID' NA = 'NA' COST_CHOICES = ( (FREE, 'Free'), (PAID, 'Paid'), (NA, 'n/a'), ) NATIVE = 'NT' MSL = 'MSL' TICKET_TYPE_CHOICES = ( (NA, 'n/a'), (NATIVE, 'Native'), (MSL, 'MSL'), ) SOFT_DRINKS_ALCOHOL = 'AV' NO_ALCOHOL = 'NO' NOT_ALCOHOL_FOCUSED = 'NF' ALCOHOL_CHOICES = ( (SOFT_DRINKS_ALCOHOL, 'Soft drinks & alcohol available'), (NO_ALCOHOL, 'No alcohol'), (NOT_ALCOHOL_FOCUSED, 'Not alcohol focused'), ) LIMITED_AVAILABILITY = 'LA' SOLD_OUT = 'SO' TICKET_LEVEL_CHOICES = ( (NA, 'Not applicable'), (LIMITED_AVAILABILITY, 'Limited availability'), (SOLD_OUT, 'Sold out'), ) parent = models.ForeignKey('self', default=None, null=True, blank=True, related_name='children', on_delete=models.SET_NULL) title = models.TextField() slug = AutoSlugField(populate_from='title', unique=False) start_time = models.DateTimeField() end_time = models.DateTimeField() featured_image = models.ForeignKey(MatteImage, null=True, blank=True, on_delete=models.SET_NULL) url = models.URLField(blank=True, default='') social_facebook = models.URLField(blank=True, default='') kicker = models.CharField(max_length=255, default='', blank=True) location_display = models.CharField(max_length=255, default='', blank=True) embargo_until = models.DateTimeField(null=True, blank=True) venue = models.ForeignKey(Venue, blank=True, null=True, on_delete=models.SET_NULL) short_description = models.TextField(default='') body = RichTextField(default='', blank=True) student_group = models.ForeignKey(StudentGroup, null=True, blank=True, default=None, on_delete=models.SET_NULL) is_over_18_only = models.BooleanField(default=False) ticket_level = models.CharField(max_length=2, choices=TICKET_LEVEL_CHOICES, default=NA) cost = models.CharField(max_length=10, choices=COST_CHOICES, default=NA) alcohol = models.CharField(max_length=2, choices=ALCOHOL_CHOICES, default=NOT_ALCOHOL_FOCUSED) ticket_type = models.CharField(max_length=3, choices=TICKET_TYPE_CHOICES, default=NA) ticket_data = models.TextField(default='', blank=True) suitable_kids_families = models.BooleanField(default=False) just_for_pgs = models.BooleanField(default=False) bundle = models.ForeignKey(Bundle, null=True, blank=True, on_delete=models.SET_NULL) brand = models.ForeignKey(BrandingPeriod, null=True, blank=True, on_delete=models.SET_NULL) category = models.ForeignKey(Category, null=True, blank=True, on_delete=models.SET_NULL) type = models.ForeignKey(Type, null=True, blank=True, on_delete=models.SET_NULL) custom_panels = [ MultiFieldPanel([ FieldPanel('title', classname='title'), FieldRowPanel([ FieldPanel('start_time'), FieldPanel('end_time'), ]), FieldPanel('embargo_until'), ImageChooserPanel('featured_image') ], heading='The basics'), MultiFieldPanel([ FieldPanel('kicker'), FieldPanel('short_description'), FieldPanel('body'), FieldPanel('location_display'), FieldPanel('venue'), ], heading='More details'), MultiFieldPanel([ FieldPanel('social_facebook'), FieldPanel('url'), ], heading='Social'), MultiFieldPanel([ FieldPanel('is_over_18_only'), FieldPanel('ticket_level'), FieldPanel('cost'), FieldPanel('alcohol'), FieldPanel('suitable_kids_families'), FieldPanel('just_for_pgs'), ], heading='About the event itself'), MultiFieldPanel([ FieldPanel('ticket_type'), FieldPanel('ticket_data'), ], heading='Ticketing'), MultiFieldPanel([ FieldPanel('bundle'), FieldPanel('brand'), FieldPanel('category'), FieldPanel('type'), FieldPanel('parent') ], heading='Organisation'), ] edit_handler = ObjectList(custom_panels) def get_msl_event_id(self): try: return MSLEvent.objects.get(event=self).msl_event_id except MSLEvent.DoesNotExist: return None def get_linked_meta(self): return LinkedMetadata(title='{} | What\'s on'.format(self.title), description=self.short_description, path='/whats-on/{}-{}'.format( self.slug, self.pk), image_resource=self.featured_image) def move_under(self, parent, user): if not user.has_perm('events.change_event'): return False if parent.pk == self.pk: return False if parent.is_top_level() and not self.has_child_events(): self.parent = parent return True else: return False def is_top_level(self): return self.parent is None def has_child_events(self): return self.children.count() > 0 def __str__(self): return self.title
class PlanetaryBodyMixin(OrbitalMechanicsMixin): """ A mixin for planetary bodies, with properties like density and gravity. Note: moons are a form of planetary body too! """ mass = MultiQuantityField(units=(ureg.earth_mass, ureg.jovian_mass, ureg.kilogram)) radius = MultiQuantityField(units=(ureg.earth_radius, ureg.jovian_radius, ureg.km, ureg.mile, ureg.m)) @property def mass_is_jovian(self): return self.mass.to("jovian_mass").magnitude > 0.1 @property def mass_is_terran(self): return self.mass.to("earth_mass").magnitude > 0.001 @property def radius_is_jovian(self): return self.radius.to("jovian_radius").magnitude > 0.1 @property def radius_is_terran(self): return self.radius.to("earth_radius").magnitude > 0.001 @property def surface_gravity(self): return GRAVITATIONAL_CONSTANT * self.mass / self.radius**2 @property def density(self): return self.mass / ((4/3) * math.pi * self.radius**3) @property def escape_velocity(self): return (2 * GRAVITATIONAL_CONSTANT * self.mass / self.radius)**0.5 @property def mean_surface_temperature(self): a_c = math.pi * self.radius**2 a_e = 4 * math.pi * self.radius**2 return ((self.solar_constant * a_c)/(STEFAN_CONSTANT * a_e))**0.25 # return self.orbited_object.luminosity**0.25 / self.semi_major_axis**2 @property def solar_constant(self): return self.orbited_object.luminosity / (4 * math.pi * self.semi_major_axis ** 2) @property def seasonal_insolation(self): return { 'equator': { 'vernal_equinox': self.surface_insolation(Q_(0, ureg.degree), Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(Q_(0, ureg.degree), Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(Q_(0, ureg.degree), Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(Q_(0, ureg.degree), Q_(270, ureg.degree)) }, 'north_tropic': { 'vernal_equinox': self.surface_insolation(self.tropics, Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(self.tropics, Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(self.tropics, Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(self.tropics, Q_(270, ureg.degree)) }, 'north_polar_circle': { 'vernal_equinox': self.surface_insolation(self.polar_circles, Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(self.polar_circles, Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(self.polar_circles, Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(self.polar_circles, Q_(270, ureg.degree)) }, 'north_pole': { 'vernal_equinox': self.surface_insolation(Q_(90, ureg.degree), Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(Q_(90, ureg.degree), Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(Q_(90, ureg.degree), Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(Q_(90, ureg.degree), Q_(270, ureg.degree)) }, 'south_tropic': { 'vernal_equinox': self.surface_insolation(-self.tropics, Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(-self.tropics, Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(-self.tropics, Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(-self.tropics, Q_(270, ureg.degree)) }, 'south_polar_circle': { 'vernal_equinox': self.surface_insolation(-self.polar_circles, Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(-self.polar_circles, Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(-self.polar_circles, Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(-self.polar_circles, Q_(270, ureg.degree)) }, 'south_pole': { 'vernal_equinox': self.surface_insolation(Q_(-90, ureg.degree), Q_(0, ureg.degree)), 'summer_solstice': self.surface_insolation(Q_(-90, ureg.degree), Q_(90, ureg.degree)), 'autumnal_equinox': self.surface_insolation(Q_(-90, ureg.degree), Q_(180, ureg.degree)), 'winter_solstice': self.surface_insolation(Q_(-90, ureg.degree), Q_(270, ureg.degree)) } } def declination(self, true_anomaly): """ Calculates the declination at a given point in the orbit. We assume that the vernal equinox is at 0. """ return self.obliquity * math.sin(true_anomaly) def sunrise_hour_angle(self, latitude, true_anomaly): """ Calculate the hour angle at which the sun rises for this body at this latitude and orbital_position. If the sun never rises here (for example at the poles in winter), then returns None. """ cos_h0 = -1 * math.tan(latitude) * math.tan(self.declination(Q_(180, ureg.degree)- true_anomaly)) if cos_h0 > 1: return math.pi * ureg.radian elif cos_h0 < -1: return None else: return math.acos(cos_h0) * ureg.radian def surface_insolation(self, latitude, true_anomaly): """ Calculate the amount of radiation received at a given point on the surface of this planet, assuming no atmosphere. """ distance_ratio = 1 + float(self.eccentricity) * math.cos(true_anomaly - self.longitude_of_periapsis) hour_angle_0 = self.sunrise_hour_angle(latitude, true_anomaly) if hour_angle_0 is None: return Q_(0, ureg.watt / ureg.meter**2) else: return self.solar_constant/math.pi * distance_ratio**2 * ( hour_angle_0 * math.sin(latitude) * math.sin(self.declination(true_anomaly)) + math.cos(latitude) * math.cos(self.declination(true_anomaly)) * math.sin(hour_angle_0) ) content_panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('mass'), FieldPanel('radius'), ]), ], 'Physical properties')] + OrbitalMechanicsMixin.content_panels class Meta: abstract = True
class HighPotentialOpportunityDetailPage(BaseInvestPage): subpage_types = ['invest.HighPotentialOpportunityDetailPage'] view_path = 'high-potential-opportunities/' breadcrumbs_label = models.CharField(max_length=50) heading = models.CharField(max_length=255) hero_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') featured = models.BooleanField(default=True) description = models.TextField( blank=True, help_text="This is the description shown when the HPO " "is featured on another page i.e. the Invest Home Page") contact_proposition = MarkdownField( blank=False, verbose_name='Body text', ) contact_button = models.CharField(max_length=500) proposition_one = MarkdownField(blank=False) proposition_one_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') proposition_one_video = models.ForeignKey('wagtailmedia.Media', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') opportunity_list_title = models.CharField(max_length=300) opportunity_list_item_one = MarkdownField() opportunity_list_item_two = MarkdownField() opportunity_list_item_three = MarkdownField(blank=True) opportunity_list_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') proposition_two = MarkdownField(blank=False) proposition_two_list_item_one = MarkdownField() proposition_two_list_item_two = MarkdownField() proposition_two_list_item_three = MarkdownField() proposition_two_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') proposition_two_video = models.ForeignKey('wagtailmedia.Media', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') competitive_advantages_title = models.CharField( max_length=300, verbose_name='Body text', ) competitive_advantages_list_item_one = MarkdownField() competitive_advantages_list_item_one_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') competitive_advantages_list_item_two = MarkdownField() competitive_advantages_list_item_two_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') competitive_advantages_list_item_three = MarkdownField() competitive_advantages_list_item_three_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') testimonial = MarkdownField(blank=True) testimonial_background = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Background image', ) companies_list_text = MarkdownField() companies_list_item_image_one = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_two = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_three = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_four = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_five = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_six = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_seven = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') companies_list_item_image_eight = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') case_study_list_title = models.CharField(max_length=300) case_study_one_text = MarkdownField(blank=True) case_study_one_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') case_study_two_text = MarkdownField(blank=True) case_study_two_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') case_study_three_text = MarkdownField(blank=True) case_study_three_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') case_study_four_text = MarkdownField(blank=True) case_study_four_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') other_opportunities_title = models.CharField(max_length=300, verbose_name='Title') pdf_document = models.ForeignKey('wagtaildocs.Document', null=True, on_delete=models.SET_NULL, related_name='+') summary_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text=( 'Image used on the opportunity listing page for this opportunity'), verbose_name='Image', ) content_panels = [ MultiFieldPanel(heading='Hero', children=[ FieldPanel('breadcrumbs_label'), FieldPanel('heading'), ImageChooserPanel('hero_image'), ]), MultiFieldPanel(heading='Featured Description', children=[FieldPanel('description')]), MultiFieldPanel(heading='Contact us', children=[ FieldRowPanel(children=[ FieldPanel('contact_proposition'), FieldPanel('contact_button'), ]) ]), MultiFieldPanel(heading='Proposition one', children=[ FieldRowPanel(children=[ FieldPanel('proposition_one'), MultiFieldPanel(children=[ ImageChooserPanel('proposition_one_image'), FieldPanel('proposition_one_video', widget=AdminMediaChooser), ]) ]) ]), MultiFieldPanel(heading='Opportunity list', children=[ FieldPanel('opportunity_list_title'), FieldRowPanel(children=[ MultiFieldPanel(children=[ FieldPanel('opportunity_list_item_one'), FieldPanel('opportunity_list_item_two'), FieldPanel('opportunity_list_item_three'), ]), ImageChooserPanel('opportunity_list_image'), ]) ]), MultiFieldPanel( heading='Opportunity list', children=[ FieldRowPanel(children=[ MultiFieldPanel(children=[ FieldPanel('proposition_two'), FieldPanel('proposition_two_list_item_one'), FieldPanel('proposition_two_list_item_two'), FieldPanel('proposition_two_list_item_three'), ]), MultiFieldPanel(children=[ ImageChooserPanel('proposition_two_image'), FieldPanel('proposition_two_video', widget=AdminMediaChooser), ]) ]) ]), MultiFieldPanel( heading='Key facts', children=[ FieldPanel('competitive_advantages_title'), FieldRowPanel(children=[ FieldPanel('competitive_advantages_list_item_one'), FieldPanel('competitive_advantages_list_item_two'), FieldPanel('competitive_advantages_list_item_three'), ]), FieldRowPanel(children=[ ImageChooserPanel( 'competitive_advantages_list_item_one_icon'), ImageChooserPanel( 'competitive_advantages_list_item_two_icon'), ImageChooserPanel( 'competitive_advantages_list_item_three_icon'), ]) ]), MultiFieldPanel(heading='Testimonial', children=[ FieldPanel('testimonial'), ImageChooserPanel('testimonial_background'), ]), MultiFieldPanel( heading='Company list', children=[ FieldPanel('companies_list_text'), FieldRowPanel(children=[ ImageChooserPanel('companies_list_item_image_one'), ImageChooserPanel('companies_list_item_image_two'), ImageChooserPanel('companies_list_item_image_three'), ImageChooserPanel('companies_list_item_image_four'), ]), FieldRowPanel(children=[ ImageChooserPanel('companies_list_item_image_five'), ImageChooserPanel('companies_list_item_image_six'), ImageChooserPanel('companies_list_item_image_seven'), ImageChooserPanel('companies_list_item_image_eight'), ]) ]), MultiFieldPanel(heading='Case studies', children=[ FieldPanel('case_study_list_title'), FieldRowPanel(children=[ FieldPanel('case_study_one_text'), ImageChooserPanel('case_study_one_image'), ]), FieldRowPanel(children=[ FieldPanel('case_study_two_text'), ImageChooserPanel('case_study_two_image'), ]), FieldRowPanel(children=[ FieldPanel('case_study_three_text'), ImageChooserPanel('case_study_three_image'), ]), FieldRowPanel(children=[ FieldPanel('case_study_four_text'), ImageChooserPanel('case_study_four_image'), ]) ]), MultiFieldPanel(heading='Other opportunities', children=[ FieldPanel('other_opportunities_title'), ]), MultiFieldPanel( heading='Summary', children=[ImageChooserPanel('summary_image')], ), SearchEngineOptimisationPanel(), ] settings_panels = [ FieldPanel('title_en_gb'), FieldPanel('slug'), FieldPanel('featured'), DocumentChooserPanel('pdf_document'), ] edit_handler = make_translated_interface(content_panels=content_panels, settings_panels=settings_panels)
class Position(models.Model): """Position represents the execution of a role within UTN""" class Meta: verbose_name = _('Position') verbose_name_plural = _('Positions') default_permissions = () ordering = ['role'] role = models.ForeignKey( 'Role', related_name='positions', on_delete=models.PROTECT, blank=False, ) recruitment_start = models.DateField( verbose_name=_('Start of recruitment'), default=date.today, ) recruitment_end = models.DateField(verbose_name=_('Recruitment deadline')) # ---- Appointment Information ------ appointments = models.IntegerField( verbose_name=_('Number of appointments'), help_text=_('Enter the number of concurrent appointments to the ' 'position'), default=1, ) term_from = models.DateField(verbose_name=_('Date of appointment')) term_to = models.DateField(verbose_name=_('End date of appointment')) comment_en = models.TextField( verbose_name=_('English extra comments'), help_text=_('Enter extra comments specific to the position this ' 'year.'), blank=True, ) comment_sv = models.TextField( verbose_name=_('Swedish extra comments'), help_text=_('Enter extra comments specific to the position this ' 'year.'), blank=True, ) comment = TranslatedField('comment_en', 'comment_sv') def __str__(self) -> str: if self.term_from.year != self.term_to.year: return "%s %s-%s" \ % (self.role.name, self.term_from.year, self.term_to.year) else: return "%s %s" % (self.role.name, self.term_from.year) @property def appointed_applications(self): Application = apps.get_model('involvement', 'Application') return Application.objects.filter( status='appointed', position=self, ) @property def is_past_due(self): return date.today() > self.recruitment_end def current_action(self) -> str: if self.is_past_due: applications = self.applications.exclude(status='draft') if applications.all().filter(status='submitted').exists(): return 'approve' elif applications.all().filter(status='appointed') \ .count() >= self.appointments: return 'done' else: return 'appoint' else: return 'recruit' # ------ Administrator settings ------ panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('role'), FieldPanel('appointments'), ]), FieldRowPanel([ FieldPanel('term_from'), FieldPanel('term_to'), ]), FieldRowPanel([ FieldPanel('recruitment_start'), FieldPanel('recruitment_end'), ]), FieldPanel('comment_en'), FieldPanel('comment_sv'), ]) ]
related_name='+') error_image = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, related_name='+', null=True, blank=True) class Meta: verbose_name = 'Algemene informatie' WebsiteSettings.panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('email', classname='col6'), FieldPanel('telefoon', classname='col6') ]), FieldRowPanel([FieldPanel('locatie', classname='col6')]), FieldRowPanel([ FieldPanel('btw_nummer', classname='col6'), ]), FieldPanel('tagline') ], heading='Algemene informatie'), MultiFieldPanel([ImageChooserPanel('error_image')], heading='Error pages (404 / 500)', classname='collapsible') ] class HomePage(AbstractEmailForm):
class RegistrationFormPage(AbstractEmailForm): # When creating a new Form page in Wagtail registration_head = models.CharField(null=True, blank=False, max_length=255) registration_newsletter_text = models.CharField(null=True, blank=False, max_length=255) registration_privacy_text = RichTextField( null=True, blank=False, features=[ 'bold', 'italic', 'underline', 'strikethrough', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'ol', 'ul', 'hr', 'embed', 'link', 'superscript', 'subscript', 'document-link', 'image', 'code' ]) registration_info_text = RichTextField( null=True, blank=False, features=[ 'bold', 'italic', 'underline', 'strikethrough', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'ol', 'ul', 'hr', 'embed', 'link', 'superscript', 'subscript', 'document-link', 'image', 'code' ]) registration_button = models.ForeignKey('home.Button', null=True, blank=False, on_delete=models.SET_NULL, related_name='+') registration_step_text = RichTextField( null=True, blank=False, features=[ 'bold', 'italic', 'underline', 'strikethrough', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'ol', 'ul', 'hr', 'embed', 'link', 'superscript', 'subscript', 'document-link', 'image', 'code' ]) thank_you_text = RichTextField( null=True, blank=False, features=[ 'bold', 'italic', 'underline', 'strikethrough', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'ol', 'ul', 'hr', 'embed', 'link', 'superscript', 'subscript', 'document-link', 'image', 'code' ]) supported_gitlabs = StreamField([ ('gitlab_server', Gitlab_Server(null=True, blank=False, icon='home')), ], null=True, blank=False) content_panels = AbstractEmailForm.content_panels + [ MultiFieldPanel( [ FieldPanel('registration_head', classname="full title"), FieldPanel('registration_newsletter_text', classname="full"), FieldPanel('registration_privacy_text', classname="full"), FieldPanel('registration_info_text', classname="full"), FieldPanel('registration_step_text', classname="full"), SnippetChooserPanel('registration_button', classname="full"), FieldPanel('thank_you_text', classname="full"), StreamFieldPanel('supported_gitlabs'), ], heading="content", ), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel("subject"), ], heading="Email Settings"), MultiFieldPanel( [InlinePanel('form_fields', label="Form fields")], heading="data", ) ] def get_submission_class(self): return RegistrationFormSubmission # Create a new user def create_user(self, username, customer_id, telephone, address, city, postal_code, email, country, newsletter, platform_data, sources, verified, first_name, last_name, password, registration_data, gift_code): # enter the data here user = get_user_model()( username=username, is_customer=True, is_active=False, customer_id=customer_id, registration_data=registration_data, ) user.set_password(password) parent_page = Page.objects.get(url_path="/home/registration/").specific profile_page = ProfilePage(title=f"{user.username}", slug=f"{user.username}", username=f"{user.username}", telephone=telephone, address=address, city=city, postal_code=postal_code, email=email, country=country, newsletter=newsletter, platform_data=platform_data, sources=sources, verified=verified, available_for_hire=verified, first_name=first_name, last_name=last_name, website=f"https://erebos.xyz", company=f"f") if gift_code: gift = GiftCode.objects.get(pk=f'{gift_code}') if gift.is_active: profile_page = ProfilePage( title=f"{user.username}", slug=f"{user.username}", username=f"{user.username}", telephone=telephone, address=address, city=city, postal_code=postal_code, email=email, country=country, newsletter=newsletter, platform_data=platform_data, sources=sources, verified=True, available_for_hire=verified, first_name=first_name, last_name=last_name, website=f"https://erebos.xyz", company=f"f", bids='{' + '"bids":[' + f'"{gift.bid}"' + ']}', tids='{' + '"tids":[' + f'"{gift.tid}"' + ']}', ) gift.is_active = False gift.save() else: profile_page = ProfilePage(title=f"{user.username}", slug=f"{user.username}", username=f"{user.username}", telephone=telephone, address=address, city=city, postal_code=postal_code, email=email, country=country, newsletter=newsletter, platform_data=platform_data, sources=sources, verified=verified, available_for_hire=verified, first_name=first_name, last_name=last_name, website=f"https://erebos.xyz", company=f"f") user.save() parent_page.add_child(instance=profile_page) profile_page.save_revision().publish() return user # Called when a user registers def send_mail(self, form): addresses = [x.strip() for x in self.to_address.split(',')] emailheader = "New registration via Pharmaziegasse Website" content = [] for field in form: value = field.value() if isinstance(value, list): value = ', '.join(value) content.append('{}: {}'.format(field.label, value)) content = '\n'.join(content) content += '\n\nMade with ❤ by a tiny SNEK' #emailfooter = '<style>@keyframes pulse { 10% { color: red; } }</style><p>Made with <span style="width: 20px; height: 1em; color:#dd0000; animation: pulse 1s infinite;">❤</span> by <a style="color: lightgrey" href="https://www.aichner-christian.com" target="_blank">Werbeagentur Christian Aichner</a></p>' #html_message = f"{emailheader}\n\n{content}\n\n{emailfooter}" send_mail(self.subject, f"{emailheader}\n\n{content}", addresses, self.from_address) def process_form_submission(self, form): user = self.create_user( username=form.cleaned_data['username'], customer_id=form.cleaned_data['customer_id'], telephone=form.cleaned_data['telephone'], address=form.cleaned_data['address'], city=form.cleaned_data['city'], postal_code=form.cleaned_data['postal_code'], email=form.cleaned_data['email'], country=form.cleaned_data['country'], newsletter=form.cleaned_data['newsletter'], platform_data=form.cleaned_data['platform_data'], sources=form.cleaned_data['sources'], verified=form.cleaned_data['verified'], first_name=form.cleaned_data['first_name'], last_name=form.cleaned_data['last_name'], password=form.cleaned_data['password'], gift_code=form.cleaned_data['gift_code'], registration_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder), ) self.get_submission_class().objects.create( form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder), page=self, user=user, ) if self.to_address: self.send_mail(form)
class Form(CollectionMember, index.Indexed, ClusterableModel): form_builder = CustomFormBuilder base_form_class = CustomWagtailAdminFormPageForm template = 'customform/customform.html' title = models.CharField( max_length=255 ) display_title = models.BooleanField( default=False, help_text=_("If you wish to display the title above the form fields tick this.") ) to_address = models.CharField( verbose_name=_('to address'), max_length=255, blank=True, help_text=_("Optional - form submissions will be emailed to these addresses. Separate multiple addresses by commas.") ) from_address = models.CharField( verbose_name=_('from address'), max_length=255, blank=True ) subject = models.CharField( verbose_name=_('subject'), max_length=255, blank=True ) uploaded_by_user = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name=_('uploaded by user'), null=True, blank=True, editable=False, on_delete=models.SET_NULL ) form_template = models.CharField( choices=FORM_TEMPLATE_CHOICES, null=True, blank=True, max_length=255 ) button_alignment = models.CharField( choices=( ('left', 'Left'), ('right', 'Right'), ('center', 'Center'), ), null=True, blank=True, max_length=255 ) created_at = models.DateTimeField(verbose_name=_('created at'), auto_now_add=True) panels = [ FieldPanel('title', classname="full"), FieldPanel('display_title'), FieldPanel('form_template'), FieldPanel('button_alignment'), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel('subject'), ], "Email"), InlinePanel('form_fields', label="Form fields"), ] search_fields = CollectionMember.search_fields + [ index.SearchField('title', partial_match=True, boost=10), index.FilterField('title'), index.FilterField('to_address', partial_match=True), index.FilterField('from_address', partial_match=True), index.FilterField('subject', partial_match=True), ] admin_form_fields = ( 'title', 'to_address', 'from_address', 'subject', 'collection', # 'form_fields' ) objects = FormQuerySet.as_manager() def __str__(self): return self.title def __init__(self, *args, **kwargs): super(Form, self).__init__(*args, **kwargs) if not hasattr(self, 'landing_page_template'): name, ext = os.path.splitext(self.template) self.landing_page_template = name + '_landing' + ext def process_form_submission(self, form): submission = self.get_submission_class().objects.create( form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder), form=self, ) if self.to_address: self.send_mail(form) return submission def send_mail(self, form): addresses = [x.strip() for x in self.to_address.split(',')] content = [] for field in form: if not isinstance(field.field, CaptchaField): value = field.value() if isinstance(value, list): value = ', '.join(value) content.append('{}: {}'.format(field.label, value)) content = '\n'.join(content) send_mail(self.subject, content, addresses, self.from_address,) def get_form_fields(self): """ Form page expects `form_fields` to be declared. If you want to change backwards relation name, you need to override this method. """ return self.form_fields.all() def get_data_fields(self): """ Returns a list of tuples with (field_name, field_label). """ data_fields = [ ('submit_time', _('Submission date')), ] data_fields += [ (field.clean_name, field.label) for field in self.get_form_fields() ] return data_fields def get_form_class(self): fb = self.form_builder(self.get_form_fields()) return fb.get_form_class() def get_form_parameters(self): return {} def get_form(self, *args, **kwargs): form_class = self.get_form_class() form_params = self.get_form_parameters() form_params.update(kwargs) return form_class(*args, **form_params) def get_submission_class(self): return FormSubmission
class InternationalCampaignPage(BasePage): service_name_value = cms.GREAT_INTERNATIONAL parent_page_types = [ 'great_international.InternationalArticleListingPage', 'great_international.InternationalTopicLandingPage', 'great_international.InternationalLocalisedFolderPage' ] subpage_types = [ 'great_international.InternationalArticlePage' ] view_path = 'campaigns/' campaign_teaser = models.CharField(max_length=255, null=True, blank=True) campaign_heading = models.CharField(max_length=255) campaign_hero_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) section_one_heading = models.CharField(max_length=255) section_one_intro = MarkdownField() section_one_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) selling_point_one_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) selling_point_one_heading = models.CharField(max_length=255) selling_point_one_content = MarkdownField() selling_point_two_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) selling_point_two_heading = models.CharField( max_length=255, null=True, blank=True, ) selling_point_two_content = MarkdownField(null=True, blank=True) selling_point_three_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) selling_point_three_heading = models.CharField( max_length=255, null=True, blank=True ) selling_point_three_content = MarkdownField(null=True, blank=True) section_one_contact_button_url = models.CharField( max_length=255, null=True, blank=True ) section_one_contact_button_text = models.CharField( max_length=255, null=True, blank=True ) section_two_heading = models.CharField(max_length=255) section_two_intro = MarkdownField() section_two_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) section_two_contact_button_url = models.CharField( max_length=255, null=True, blank=True ) section_two_contact_button_text = models.CharField( max_length=255, null=True, blank=True ) related_content_heading = models.CharField(max_length=255) related_content_intro = MarkdownField() related_page_one = models.ForeignKey( 'great_international.InternationalArticlePage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) related_page_two = models.ForeignKey( 'great_international.InternationalArticlePage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) related_page_three = models.ForeignKey( 'great_international.InternationalArticlePage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) cta_box_message = models.CharField(max_length=255) cta_box_button_url = models.CharField(max_length=255) cta_box_button_text = models.CharField(max_length=255) tags = ParentalManyToManyField(Tag, blank=True) content_panels = [ MultiFieldPanel( heading='Hero section', children=[ FieldPanel('campaign_heading'), FieldPanel('campaign_teaser'), ImageChooserPanel('campaign_hero_image'), ] ), MultiFieldPanel( heading='Section one', children=[ FieldPanel('section_one_heading'), FieldPanel('section_one_intro'), ImageChooserPanel('section_one_image'), FieldRowPanel([ MultiFieldPanel( children=[ ImageChooserPanel('selling_point_one_icon'), FieldPanel('selling_point_one_heading'), FieldPanel('selling_point_one_content'), ] ), MultiFieldPanel( children=[ ImageChooserPanel('selling_point_two_icon'), FieldPanel('selling_point_two_heading'), FieldPanel('selling_point_two_content'), ] ), MultiFieldPanel( children=[ ImageChooserPanel('selling_point_three_icon'), FieldPanel('selling_point_three_heading'), FieldPanel('selling_point_three_content'), ] ), ]), FieldRowPanel([ FieldPanel('section_one_contact_button_text'), FieldPanel('section_one_contact_button_url'), ]) ] ), MultiFieldPanel( heading='Section two', children=[ FieldPanel('section_two_heading'), FieldPanel('section_two_intro'), ImageChooserPanel('section_two_image'), FieldRowPanel([ FieldPanel('section_two_contact_button_text'), FieldPanel('section_two_contact_button_url'), ]) ] ), MultiFieldPanel( heading='Related content section', children=[ FieldPanel('related_content_heading'), FieldPanel('related_content_intro'), FieldRowPanel([ PageChooserPanel( 'related_page_one', 'great_international.InternationalArticlePage'), PageChooserPanel( 'related_page_two', 'great_international.InternationalArticlePage'), PageChooserPanel( 'related_page_three', 'great_international.InternationalArticlePage'), ]) ] ), MultiFieldPanel( heading='Contact box', children=[ FieldRowPanel([ FieldPanel('cta_box_message', widget=Textarea), MultiFieldPanel([ FieldPanel('cta_box_button_url'), FieldPanel('cta_box_button_text'), ]) ]) ] ), SearchEngineOptimisationPanel(), ] settings_panels = [ FieldPanel('title_en_gb'), FieldPanel('slug'), FieldPanel('tags', widget=CheckboxSelectMultiple) ]
class InternationalArticlePage(BasePage): service_name_value = cms.GREAT_INTERNATIONAL parent_page_types = [ 'great_international.InternationalArticleListingPage', 'great_international.InternationalCampaignPage', 'great_international.InternationalLocalisedFolderPage' ] subpage_types = [] article_title = models.CharField(max_length=255) article_teaser = models.CharField(max_length=255) article_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) article_body_text = MarkdownField() related_page_one = models.ForeignKey( 'great_international.InternationalArticlePage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) related_page_two = models.ForeignKey( 'great_international.InternationalArticlePage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) related_page_three = models.ForeignKey( 'great_international.InternationalArticlePage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) tags = ParentalManyToManyField(Tag, blank=True) content_panels = [ FieldPanel('article_title'), MultiFieldPanel( heading='Article content', children=[ FieldPanel('article_teaser'), ImageChooserPanel('article_image'), FieldPanel('article_body_text') ] ), MultiFieldPanel( heading='Related articles', children=[ FieldRowPanel([ PageChooserPanel( 'related_page_one', 'great_international.InternationalArticlePage'), PageChooserPanel( 'related_page_two', 'great_international.InternationalArticlePage'), PageChooserPanel( 'related_page_three', 'great_international.InternationalArticlePage'), ]), ] ), SearchEngineOptimisationPanel(), ] settings_panels = [ FieldPanel('title_en_gb'), FieldPanel('slug'), FieldPanel('tags', widget=CheckboxSelectMultiple) ]
class InternationalHomePage(ExclusivePageMixin, BasePage): service_name_value = cms.GREAT_INTERNATIONAL slug_identity = cms.GREAT_HOME_INTERNATIONAL_SLUG subpage_types = [] tariffs_title = models.CharField(max_length=255) tariffs_description = MarkdownField() tariffs_link = models.URLField() tariffs_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) news_title = models.CharField(max_length=255) related_page_one = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) related_page_two = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) related_page_three = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) content_panels = [ MultiFieldPanel( heading='Tariffs', children=[ FieldPanel('tariffs_title'), FieldPanel('tariffs_description'), FieldPanel('tariffs_link'), ImageChooserPanel('tariffs_image') ] ), MultiFieldPanel( heading='News section', children=[ FieldPanel('news_title'), FieldRowPanel([ PageChooserPanel( 'related_page_one', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), PageChooserPanel( 'related_page_two', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), PageChooserPanel( 'related_page_three', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), ]) ] ), SearchEngineOptimisationPanel(), ] settings_panels = [ FieldPanel('title_en_gb'), FieldPanel('slug'), ]
class InternationalSectorPage(BasePage): service_name_value = cms.GREAT_INTERNATIONAL parent_page_types = ['great_international.InternationalTopicLandingPage'] subpage_types = [] tags = ParentalManyToManyField(Tag, blank=True) heading = models.CharField(max_length=255) sub_heading = models.CharField(max_length=255) hero_image = models.ForeignKey( 'wagtailimages.Image', null=True, on_delete=models.SET_NULL, related_name='+' ) heading_teaser = models.TextField(blank=True) section_one_body = MarkdownField( null=True, verbose_name='Bullets markdown' ) section_one_image = models.ForeignKey( 'wagtailimages.Image', null=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Bullets image' ) section_one_image_caption = models.CharField( max_length=255, blank=True, verbose_name='Bullets image caption') section_one_image_caption_company = models.CharField( max_length=255, blank=True, verbose_name='Bullets image caption — company name') statistic_1_number = models.CharField(max_length=255) statistic_1_heading = models.CharField(max_length=255) statistic_1_smallprint = models.CharField(max_length=255, blank=True) statistic_2_number = models.CharField(max_length=255) statistic_2_heading = models.CharField(max_length=255) statistic_2_smallprint = models.CharField(max_length=255, blank=True) statistic_3_number = models.CharField(max_length=255, blank=True) statistic_3_heading = models.CharField(max_length=255, blank=True) statistic_3_smallprint = models.CharField(max_length=255, blank=True) statistic_4_number = models.CharField(max_length=255, blank=True) statistic_4_heading = models.CharField(max_length=255, blank=True) statistic_4_smallprint = models.CharField(max_length=255, blank=True) statistic_5_number = models.CharField(max_length=255, blank=True) statistic_5_heading = models.CharField(max_length=255, blank=True) statistic_5_smallprint = models.CharField(max_length=255, blank=True) statistic_6_number = models.CharField(max_length=255, blank=True) statistic_6_heading = models.CharField(max_length=255, blank=True) statistic_6_smallprint = models.CharField(max_length=255, blank=True) section_two_heading = models.CharField( max_length=255, verbose_name='Highlights heading' ) section_two_teaser = models.TextField( verbose_name='Highlights teaser' ) section_two_subsection_one_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Highlight 1 icon' ) section_two_subsection_one_heading = models.CharField( max_length=255, verbose_name='Highlight 1 heading' ) section_two_subsection_one_body = models.TextField( verbose_name='Highlight 1 body' ) section_two_subsection_two_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Highlight 2 icon' ) section_two_subsection_two_heading = models.CharField( max_length=255, verbose_name='Highlight 2 heading' ) section_two_subsection_two_body = models.TextField( verbose_name='Highlight 2 body' ) section_two_subsection_three_icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Highlight 3 icon' ) section_two_subsection_three_heading = models.CharField( max_length=255, verbose_name='Highlight 3 heading' ) section_two_subsection_three_body = models.TextField( verbose_name='Highlight 3 body' ) case_study_title = models.CharField(max_length=255, blank=True) case_study_description = models.CharField(max_length=255, blank=True) case_study_cta_text = models.TextField(blank=True) case_study_cta_page = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) case_study_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) section_three_heading = models.CharField( max_length=255, blank=True, verbose_name='Fact sheets heading' ) section_three_teaser = models.TextField( blank=True, verbose_name='Fact sheets teaser' ) section_three_subsection_one_heading = models.CharField( max_length=255, blank=True, verbose_name='Fact sheet 1 heading' ) section_three_subsection_one_teaser = models.TextField( blank=True, verbose_name='Fact sheet 1 teaser' ) section_three_subsection_one_body = MarkdownField( blank=True, null=True, verbose_name='Fact sheet 1 body' ) section_three_subsection_two_heading = models.CharField( max_length=255, blank=True, verbose_name='Fact sheet 2 heading' ) section_three_subsection_two_teaser = models.TextField( blank=True, verbose_name='Fact sheet 2 teaser' ) section_three_subsection_two_body = MarkdownField( blank=True, null=True, verbose_name='Fact sheet 2 body' ) related_page_one = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) related_page_two = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) related_page_three = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) content_panels = [ MultiFieldPanel( heading='Heading', children=[ FieldPanel('heading'), FieldPanel('sub_heading'), ImageChooserPanel('hero_image'), FieldPanel('heading_teaser') ] ), MultiFieldPanel( heading='Bullets', children=[ HelpPanel( 'For accessibility reasons, use only "## [Your text here]"' ' for headings in this markdown field'), FieldRowPanel( [ FieldPanel('section_one_body'), MultiFieldPanel( [ ImageChooserPanel('section_one_image'), FieldPanel('section_one_image_caption'), FieldPanel('section_one_image_caption_company') ] ) ] ) ] ), MultiFieldPanel( heading='Statistics', children=[ FieldRowPanel( [ MultiFieldPanel( [ FieldPanel('statistic_1_number'), FieldPanel('statistic_1_heading'), FieldPanel('statistic_1_smallprint') ] ), MultiFieldPanel( [ FieldPanel('statistic_2_number'), FieldPanel('statistic_2_heading'), FieldPanel('statistic_2_smallprint') ] ), MultiFieldPanel( [ FieldPanel('statistic_3_number'), FieldPanel('statistic_3_heading'), FieldPanel('statistic_3_smallprint') ] ), MultiFieldPanel( [ FieldPanel('statistic_4_number'), FieldPanel('statistic_4_heading'), FieldPanel('statistic_4_smallprint') ] ), MultiFieldPanel( [ FieldPanel('statistic_5_number'), FieldPanel('statistic_5_heading'), FieldPanel('statistic_5_smallprint') ] ), MultiFieldPanel( [ FieldPanel('statistic_6_number'), FieldPanel('statistic_6_heading'), FieldPanel('statistic_6_smallprint') ] ), ] ) ] ), MultiFieldPanel( heading='Highlights', children=[ FieldPanel('section_two_heading'), FieldPanel('section_two_teaser'), FieldRowPanel( [ MultiFieldPanel( [ ImageChooserPanel( 'section_two_subsection_one_icon'), FieldPanel( 'section_two_subsection_one_heading'), FieldPanel( 'section_two_subsection_one_body') ] ), MultiFieldPanel( [ ImageChooserPanel( 'section_two_subsection_two_icon'), FieldPanel( 'section_two_subsection_two_heading'), FieldPanel( 'section_two_subsection_two_body') ] ), MultiFieldPanel( [ ImageChooserPanel( 'section_two_subsection_three_icon'), FieldPanel( 'section_two_subsection_three_heading'), FieldPanel( 'section_two_subsection_three_body') ] ) ] ) ] ), MultiFieldPanel( heading='Case Study', classname='collapsible', children=[ FieldPanel('case_study_title'), FieldPanel('case_study_description'), FieldPanel('case_study_cta_text'), PageChooserPanel( 'case_study_cta_page', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), ImageChooserPanel('case_study_image') ] ), MultiFieldPanel( heading='Fact Sheets', classname='collapsible collapsed', children=[ FieldPanel('section_three_heading'), FieldPanel('section_three_teaser'), FieldRowPanel( [ MultiFieldPanel( [ FieldPanel( 'section_three_subsection_one_heading'), FieldPanel( 'section_three_subsection_one_teaser'), HelpPanel( 'For accessibility reasons, use only ' '"#### [Your text here]" for subheadings ' 'in this markdown field'), FieldPanel( 'section_three_subsection_one_body') ] ), MultiFieldPanel( [ FieldPanel( 'section_three_subsection_two_heading'), FieldPanel( 'section_three_subsection_two_teaser'), HelpPanel( 'For accessibility reasons, use only ' '"#### [Your text here]" for subheadings ' 'in this markdown field'), FieldPanel( 'section_three_subsection_two_body') ] ) ] ) ] ), MultiFieldPanel( heading='Related articles', children=[ FieldRowPanel([ PageChooserPanel( 'related_page_one', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), PageChooserPanel( 'related_page_two', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), PageChooserPanel( 'related_page_three', [ 'great_international.InternationalArticlePage', 'great_international.InternationalCampaignPage', ]), ]) ] ), SearchEngineOptimisationPanel() ] settings_panels = [ FieldPanel('title_en_gb'), FieldPanel('slug'), FieldPanel('tags', widget=CheckboxSelectMultiple) ]
class Competition(RoutablePageMixin, Page): rules = RichTextField() voting = RichTextField(blank=True, null=True) allowed_points = models.CharField(max_length=128, default="0,1,2,3,4,5,6,7,8,9,10") allow_same_points = models.BooleanField(default=False) entries_per_person = models.PositiveIntegerField(default=2) entries_in_rating = models.PositiveIntegerField(default=1) submission_start = models.DateTimeField(blank=True, null=True) submission_end = models.DateTimeField(blank=True, null=True) voting_start = models.DateTimeField(blank=True, null=True) voting_end = models.DateTimeField(blank=True, null=True) content_panels = Page.content_panels + [ FieldPanel('rules'), FieldPanel('votinghelp'), FieldRowPanel( [FieldPanel('allowed_points'), FieldPanel('allow_same_points')]), FieldRowPanel([ FieldPanel('entries_per_person'), FieldPanel('entries_in_rating') ]), FieldRowPanel( [FieldPanel('submission_start'), FieldPanel('submission_end')]), FieldRowPanel([FieldPanel('voting_start'), FieldPanel('voting_end')]), ] def voting_group_id(self): group, _ = Group.objects.get_or_create( name="competition-{}-{}".format(self.id, self.slug)) return group.id def user_has_vote_access(self, user): if user is None or isinstance(user, AnonymousUser): return False try: x = user.groups.get(id=self.voting_group_id()) return True except: return False @route(r'^vote/v/(\d+)/comment/$') @method_decorator(login_required) def make_comment(self, request, entry_id=None): if not self.user_has_vote_access(request.user): return logout_then_login(request) votes = get_object_or_404(Votes, entry_id=entry_id) comments = request.POST.get('comments') votes.comments = comments votes.save() print(votes, comments) return JsonResponse({'comment': votes.comments}) @route(r'^vote/v/(\d+)/$') @route(r'^vote/v/$') @method_decorator(login_required) def my_votes_for(self, request, entry_id=None): if not self.user_has_vote_access(request.user): return logout_then_login(request) if entry_id is None: votes = Votes.objects.filter(entry__competition=self, user=request.user) res = [] for x in votes: res.append({ 'id': x.entry.id, 'points': x.points, 'name': x.entry.title, 'comments': x.comments }) return JsonResponse(res, safe=False) else: entry = get_object_or_404(EntryImage, id=entry_id) votes, _ = Votes.objects.get_or_create(entry=entry, user=request.user) if request.method == 'POST': now = timezone.now() between, _ = check_dates(entry.competition.voting_start, entry.competition.voting_end) if not between: return HttpResponseForbidden() points = request.POST.get('points') # See if voted points are allowed if int(points) > 0 and not entry.competition.allow_same_points: v = Votes.objects.filter(entry__competition=self, user=request.user, points=points) if v.count() > 0: return HttpResponseForbidden() votes.points = points votes.save() return JsonResponse({'id': entry_id, 'points': votes.points}) @route(r'^vote/$', name='vote') @method_decorator(login_required) def show_vote_form(self, request): if not self.user_has_vote_access(request.user): messages.error(request, 'You lack permissions for this Competition') return logout_then_login(request, login_url="{}vote".format(self.url)) context = {} between, future = check_dates(self.voting_start, self.voting_end) if not between: if future: messages.error(request, 'Voting starts {}'.format(self.voting_start)) else: messages.error(request, 'Voting ended {}'.format(self.voting_end)) else: context['entries'] = self.entries.all() context['points'] = self.allowed_points.split(",") context['page'] = self return render(request, 'wagtailphotovoter/vote.html', context) @route(r'^choice/$', name='choice') @method_decorator(login_required) def show_choice_form(self, request): if not self.user_has_vote_access(request.user): messages.error(request, 'You lack permissions for this Competition') return logout_then_login(request, login_url="{}vote".format(self.url)) context = {} context['page'] = self between, future = check_dates(self.voting_start, self.voting_end) if not between: if future: messages.error(request, 'Voting starts {}'.format(self.voting_start)) else: messages.error(request, 'Voting ended {}'.format(self.voting_end)) elif request.method == 'POST': for user_id in request.POST: if user_id.startswith("user_"): u_id = user_id.split("_")[1] i_id = request.POST.get(user_id) entry = EntryImage.objects.get(pk=i_id) iv, created = ImageVote.objects.get_or_create( user=request.user, entry=entry) # Find and delete other ImageVote.objects.filter(entry__user=entry.user).exclude( entry=entry).delete() return JsonResponse({"OK": "fo"}) else: votes = {} for e in ImageVote.objects.filter(user=request.user).all(): votes[e.entry.user.id] = e.entry.id context['users'] = EntryUser.objects.filter( entries__competition=self).distinct() context['votes'] = votes print(votes) return render(request, 'wagtailphotovoter/image_choice.html', context) @route(r'^result/$') @method_decorator(login_required) def show_result(self, request): if not self.user_has_vote_access(request.user): return logout_then_login(request, login_url="{}result".format(self.url)) context = {} q = self.entries.annotate(total_points=Sum('votes__points')).order_by( F('total_points').desc(nulls_last=True)).all() context['entries'] = q #print(context['entries']) context['page'] = self return render(request, 'wagtailphotovoter/result.html', context) @route(r'^result/csv/$') @method_decorator(login_required) def some_view(self, request): if not self.user_has_vote_access(request.user): return logout_then_login(request, login_url="{}result/csv".format(self.url)) response = HttpResponse(content_type='text/csv') response[ 'Content-Disposition'] = 'attachment; filename="{}-{}.csv"'.format( self.id, self.slug) writer = csv.writer(response) writer.writerow([ 'link', 'points', 'title', 'author', 'email', 'location', 'gear', 'comments' ]) q = self.entries.annotate(total_points=Sum('votes__points')).order_by( F('total_points').desc(nulls_last=True)).all() for e in q: comments = "" for v in e.votes.all(): if v.comments is not None: comments += "{}: {}\n".format(v.user, v.comments) writer.writerow([ "{}{}".format(settings.BASE_URL, e.link), e.points, e.title, e.user.name, e.user.email, e.location, e.gear, comments ]) return response @route(r'^$') def show_add_form(self, request): context = {'page': self} between, future = check_dates(self.submission_start, self.submission_end) if not between: if future: messages.error( request, 'Submissions opens {}'.format(self.submission_start)) else: messages.error( request, 'Sumbissions closed {}'.format(self.submission_end)) return render(request, 'wagtailphotovoter/add.html', context) imageFormSet = formset_factory(ImageForm, extra=self.entries_per_person, max_num=self.entries_per_person, min_num=1) if request.method == 'POST': iform = imageFormSet(request.POST, request.FILES) aform = AuthorForm(request.POST, request.FILES) if iform.is_valid() and aform.is_valid(): name = aform.cleaned_data.get('name') email = aform.cleaned_data.get('email') user, created = EntryUser.objects.get_or_create(email=email) user.name = name user.save() # get collection c = Collection.objects.filter(name=self.title).first() for i in iform: gear = i.cleaned_data.get('gear') title = i.cleaned_data.get('title') location = i.cleaned_data.get('location') # Skip if not if gear is None and title is None and location is None: continue img = EntryImage.objects.create( file=i.cleaned_data.get('photo'), title=title, competition=self, gear=gear, location=location, user=user, collection=c, ) messages.success( request, "Your entry '{}' has been submitted".format(title)) # Both forms valid, so redirect to avoid multiple submissions of same return HttpResponseRedirect(self.get_url()) else: iform = imageFormSet() aform = AuthorForm() context['author'] = aform context['images'] = iform return render(request, 'wagtailphotovoter/add.html', context)
class Logo(models.Model): class Meta: verbose_name = _('logo') verbose_name_plural = _('logos') CATEGORY_CHOICES = ( ('committee', _('Committee')), ('section', _('Section')), ) category = models.CharField( max_length=20, choices=CATEGORY_CHOICES, verbose_name=_('category'), blank=False, null=False, ) link = models.URLField( verbose_name=_('links to'), null=False, blank=False, ) logo = models.ForeignKey('wagtailimages.Image', verbose_name=_('logo'), null=True, blank=True, on_delete=models.SET_NULL, related_name='+') logo_white = models.ForeignKey('wagtailimages.Image', verbose_name=_('white logo'), null=True, blank=True, on_delete=models.SET_NULL, related_name='+') logo_black = models.ForeignKey('wagtailimages.Image', verbose_name=_('black logo'), null=True, blank=True, on_delete=models.SET_NULL, related_name='+') belongs_to = models.ForeignKey( 'wagtailcore.Site', verbose_name=_('belongs to'), null=True, blank=True, on_delete=models.SET_NULL, ) # ------ Administrator settings ------ panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('category'), FieldPanel('link'), ]), ImageChooserPanel('logo'), ImageChooserPanel('logo_white'), ImageChooserPanel('logo_black'), FieldPanel('belongs_to'), ]) ]
class EventPage(AbstractFilterPage): # General content fields body = RichTextField('Subheading', blank=True) archive_body = RichTextField(blank=True) live_body = RichTextField(blank=True) future_body = RichTextField(blank=True) persistent_body = StreamField([ ('content', blocks.RichTextBlock(icon='edit')), ('content_with_anchor', molecules.ContentWithAnchor()), ('heading', v1_blocks.HeadingBlock(required=False)), ('image', molecules.ContentImage()), ('table_block', organisms.AtomicTableBlock( table_options={'renderer': 'html'} )), ('reusable_text', v1_blocks.ReusableTextChooserBlock( 'v1.ReusableText' )), ], blank=True) start_dt = models.DateTimeField("Start") end_dt = models.DateTimeField("End", blank=True, null=True) future_body = RichTextField(blank=True) archive_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) video_transcript = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) speech_transcript = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) flickr_url = models.URLField("Flickr URL", blank=True) archive_video_id = models.CharField( 'YouTube video ID (archive)', null=True, blank=True, max_length=11, # This is a reasonable but not official regex for YouTube video IDs. # https://webapps.stackexchange.com/a/54448 validators=[RegexValidator(regex=r'^[\w-]{11}$')], help_text=organisms.VideoPlayer.YOUTUBE_ID_HELP_TEXT ) live_stream_availability = models.BooleanField( "Streaming?", default=False, blank=True, help_text='Check if this event will be streamed live. This causes the ' 'event page to show the parts necessary for live streaming.' ) live_video_id = models.CharField( 'YouTube video ID (live)', null=True, blank=True, max_length=11, # This is a reasonable but not official regex for YouTube video IDs. # https://webapps.stackexchange.com/a/54448 validators=[RegexValidator(regex=r'^[\w-]{11}$')], help_text=organisms.VideoPlayer.YOUTUBE_ID_HELP_TEXT ) live_stream_date = models.DateTimeField( "Go Live Date", blank=True, null=True, help_text='Enter the date and time that the page should switch from ' 'showing the venue image to showing the live video feed. ' 'This is typically 15 minutes prior to the event start time.' ) # Venue content fields venue_coords = models.CharField(max_length=100, blank=True) venue_name = models.CharField(max_length=100, blank=True) venue_street = models.CharField(max_length=100, blank=True) venue_suite = models.CharField(max_length=100, blank=True) venue_city = models.CharField(max_length=100, blank=True) venue_state = USStateField(blank=True) venue_zipcode = models.CharField(max_length=12, blank=True) venue_image_type = models.CharField( max_length=8, choices=( ('map', 'Map'), ('image', 'Image (selected below)'), ('none', 'No map or image'), ), default='map', help_text='If "Image" is chosen here, you must select the image you ' 'want below. It should be sized to 1416x796.', ) venue_image = models.ForeignKey( 'v1.CFGOVImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) post_event_image_type = models.CharField( max_length=16, choices=( ('placeholder', 'Placeholder image'), ('image', 'Unique image (selected below)'), ), default='placeholder', verbose_name='Post-event image type', help_text='Choose what to display after an event concludes. This will ' 'be overridden by embedded video if the "YouTube video ID ' '(archive)" field on the previous tab is populated. If ' '"Unique image" is chosen here, you must select the image ' 'you want below. It should be sized to 1416x796.', ) post_event_image = models.ForeignKey( 'v1.CFGOVImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) # Agenda content fields agenda_items = StreamField([('item', AgendaItemBlock())], blank=True) objects = CFGOVPageManager() search_fields = AbstractFilterPage.search_fields + [ index.SearchField('body'), index.SearchField('archive_body'), index.SearchField('live_video_id'), index.SearchField('flickr_url'), index.SearchField('archive_video_id'), index.SearchField('future_body'), index.SearchField('agenda_items') ] # General content tab content_panels = CFGOVPage.content_panels + [ FieldPanel('body'), FieldRowPanel([ FieldPanel('start_dt', classname="col6"), FieldPanel('end_dt', classname="col6"), ]), MultiFieldPanel([ FieldPanel('archive_body'), ImageChooserPanel('archive_image'), DocumentChooserPanel('video_transcript'), DocumentChooserPanel('speech_transcript'), FieldPanel('flickr_url'), FieldPanel('archive_video_id'), ], heading='Archive Information'), FieldPanel('live_body'), FieldPanel('future_body'), StreamFieldPanel('persistent_body'), MultiFieldPanel([ FieldPanel('live_stream_availability'), FieldPanel('live_video_id'), FieldPanel('live_stream_date'), ], heading='Live Stream Information'), ] # Venue content tab venue_panels = [ FieldPanel('venue_name'), MultiFieldPanel([ FieldPanel('venue_street'), FieldPanel('venue_suite'), FieldPanel('venue_city'), FieldPanel('venue_state'), FieldPanel('venue_zipcode'), ], heading='Venue Address'), MultiFieldPanel([ FieldPanel('venue_image_type'), ImageChooserPanel('venue_image'), ], heading='Venue Image'), MultiFieldPanel([ FieldPanel('post_event_image_type'), ImageChooserPanel('post_event_image'), ], heading='Post-event Image') ] # Agenda content tab agenda_panels = [ StreamFieldPanel('agenda_items'), ] # Promotion panels promote_panels = [ MultiFieldPanel(AbstractFilterPage.promote_panels, "Page configuration"), ] # Tab handler interface edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(venue_panels, heading='Venue Information'), ObjectList(agenda_panels, heading='Agenda Information'), ObjectList(AbstractFilterPage.sidefoot_panels, heading='Sidebar'), ObjectList(AbstractFilterPage.settings_panels, heading='Configuration'), ]) template = 'events/event.html' @property def event_state(self): if self.end_dt: end = convert_date(self.end_dt, 'America/New_York') if end < datetime.now(timezone('America/New_York')): return 'past' if self.live_stream_date: start = convert_date( self.live_stream_date, 'America/New_York' ) else: start = convert_date(self.start_dt, 'America/New_York') if datetime.now(timezone('America/New_York')) > start: return 'present' return 'future' @property def page_js(self): if ( (self.live_stream_date and self.event_state == 'present') or (self.archive_video_id and self.event_state == 'past') ): return super(EventPage, self).page_js + ['video-player.js'] return super(EventPage, self).page_js def location_image_url(self, scale='2', size='276x155', zoom='12'): if not self.venue_coords: self.venue_coords = get_venue_coords( self.venue_city, self.venue_state ) api_url = 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/static' static_map_image_url = '{}/{},{}/{}?access_token={}'.format( api_url, self.venue_coords, zoom, size, settings.MAPBOX_ACCESS_TOKEN ) return static_map_image_url def clean(self): super(EventPage, self).clean() if self.venue_image_type == 'image' and not self.venue_image: raise ValidationError({ 'venue_image': 'Required if "Venue image type" is "Image".' }) if self.post_event_image_type == 'image' and not self.post_event_image: raise ValidationError({ 'post_event_image': 'Required if "Post-event image type" is ' '"Image".' }) def save(self, *args, **kwargs): self.venue_coords = get_venue_coords(self.venue_city, self.venue_state) return super(EventPage, self).save(*args, **kwargs) def get_context(self, request): context = super(EventPage, self).get_context(request) context['event_state'] = self.event_state return context
FieldPanel('title', classname='full title'), FieldPanel('description'), ImageChooserPanel('image'), StreamFieldPanel('content'), MultiFieldPanel( [ FieldPanel('start_date'), FieldPanel('end_date'), ], heading="Your Words Competition Settings", ) ] YourWordsCompetition.settings_panels = [ MultiFieldPanel([ FieldRowPanel([FieldPanel('extra_style_hints')], classname="label-above") ], "Meta") ] class YourWordsCompetitionEntry(models.Model): competition = models.ForeignKey(YourWordsCompetition) submission_date = models.DateField(null=True, blank=True, auto_now_add=True) user = models.ForeignKey('auth.User') story_name = models.CharField(max_length=128) story_text = models.TextField() terms_or_conditions_approved = models.BooleanField() hide_real_name = models.BooleanField() is_read = models.BooleanField(default=False)
class LeagueBadgeRoundEntry(models.Model): page = ParentalKey('BadgesPage', related_name='rounds', on_delete=models.CASCADE) round_name = models.CharField(max_length=50) # Recurve rc_white_score = models.IntegerField("RC White") rc_black_score = models.IntegerField("RC Black") rc_blue_score = models.IntegerField("RC Blue") rc_red_score = models.IntegerField("RC Red") rc_gold_score = models.IntegerField("RC Gold") # Barebow bb_white_score = models.IntegerField("BB White") bb_black_score = models.IntegerField("BB Black") bb_blue_score = models.IntegerField("BB Blue") bb_red_score = models.IntegerField("BB Red") bb_gold_score = models.IntegerField("BB Gold") # Compound cb_white_score = models.IntegerField("CP White") cb_black_score = models.IntegerField("CP Black") cb_blue_score = models.IntegerField("CP Blue") cb_red_score = models.IntegerField("CP Red") cb_gold_score = models.IntegerField("CP Gold") # Longbow lb_white_score = models.IntegerField("LB White") lb_black_score = models.IntegerField("LB Black") lb_blue_score = models.IntegerField("LB Blue") lb_red_score = models.IntegerField("LB Red") lb_gold_score = models.IntegerField("LB Gold") @property def recurve_scores(self): return self.rc_white_score, self.rc_black_score, self.rc_blue_score, self.rc_red_score, self.rc_gold_score @property def barebow_scores(self): return self.bb_white_score, self.bb_black_score, self.bb_blue_score, self.bb_red_score, self.bb_gold_score @property def compound_scores(self): return self.cb_white_score, self.cb_black_score, self.cb_blue_score, self.cb_red_score, self.cb_gold_score @property def longbow_scores(self): return self.lb_white_score, self.lb_black_score, self.lb_blue_score, self.lb_red_score, self.lb_gold_score @property def table_order(self): return ("Recurve", self.recurve_scores), ("Barebow", self.barebow_scores), ("Compound", self.compound_scores), \ ("Longbow", self.longbow_scores) panels = [ FieldPanel('round_name', classname='title'), FieldRowPanel([ FieldPanel('rc_white_score', classname='col4'), FieldPanel('rc_black_score', classname='col4'), FieldPanel('rc_blue_score', classname='col4'), FieldPanel('rc_red_score', classname='col4'), FieldPanel('rc_gold_score', classname='col4') ]), FieldRowPanel([ FieldPanel('bb_white_score', classname='col4'), FieldPanel('bb_black_score', classname='col4'), FieldPanel('bb_blue_score', classname='col4'), FieldPanel('bb_red_score', classname='col4'), FieldPanel('bb_gold_score', classname='col4') ]), FieldRowPanel([ FieldPanel('cb_white_score', classname='col4'), FieldPanel('cb_black_score', classname='col4'), FieldPanel('cb_blue_score', classname='col4'), FieldPanel('cb_red_score', classname='col4'), FieldPanel('cb_gold_score', classname='col4') ]), FieldRowPanel([ FieldPanel('lb_white_score', classname='col4'), FieldPanel('lb_black_score', classname='col4'), FieldPanel('lb_blue_score', classname='col4'), FieldPanel('lb_red_score', classname='col4'), FieldPanel('lb_gold_score', classname='col4') ]), ]
class RoundBase(WorkflowStreamForm, SubmittableStreamForm): # type: ignore is_creatable = False submission_class = ApplicationSubmission objects = PageManager.from_queryset(RoundBaseManager)() subpage_types = [] # type: ignore lead = models.ForeignKey( settings.AUTH_USER_MODEL, limit_choices_to=LIMIT_TO_STAFF, related_name='%(class)s_lead', on_delete=models.PROTECT, ) reviewers = ParentalManyToManyField( settings.AUTH_USER_MODEL, related_name='%(class)s_reviewer', limit_choices_to=LIMIT_TO_REVIEWERS, blank=True, ) start_date = models.DateField(default=date.today) end_date = models.DateField( blank=True, null=True, default=date.today, help_text= 'When no end date is provided the round will remain open indefinitely.' ) sealed = models.BooleanField(default=False) content_panels = SubmittableStreamForm.content_panels + [ FieldPanel('lead'), MultiFieldPanel([ FieldRowPanel([ FieldPanel('start_date'), FieldPanel('end_date'), ]), ], heading="Dates"), FieldPanel('reviewers'), ReadOnlyPanel('get_workflow_name_display', heading="Workflow"), # Forms comes from parental key in models/forms.py ReadOnlyInlinePanel('forms', help_text="Copied from the fund."), ReadOnlyInlinePanel('review_forms', help_text="Copied from the fund."), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(SubmittableStreamForm.promote_panels, heading='Promote'), ]) def get_template(self, request, *args, **kwargs): # Make sure all children use the shared template return 'funds/round.html' def get_landing_page_template(self, request, *args, **kwargs): # Make sure all children use the shared template return 'funds/round_landing.html' @property def is_sealed(self): return self.sealed and self.is_open @property def is_open(self): return self.start_date <= date.today() <= self.end_date def save(self, *args, **kwargs): is_new = not self.id if is_new and hasattr(self, 'parent_page'): parent_page = self.parent_page[self.__class__][self.title] self.workflow_name = parent_page.workflow_name self.reviewers = parent_page.reviewers.all() super().save(*args, **kwargs) if is_new and hasattr(self, 'parent_page'): # Would be nice to do this using model clusters as part of the __init__ self._copy_forms('forms') self._copy_forms('review_forms') def _copy_forms(self, field): for form in getattr(self.get_parent().specific, field).all(): new_form = self._meta.get_field(field).related_model self._copy_form(form, new_form) def _copy_form(self, form, new_class): # Create a copy of the existing form object new_form = form.form new_form.id = None new_form.name = '{} for {} ({})'.format(new_form.name, self.title, self.get_parent().title) new_form.save() new_class.objects.create(round=self, form=new_form) def get_submit_meta_data(self, **kwargs): return super().get_submit_meta_data( page=self.get_parent(), round=self, **kwargs, ) def clean(self): super().clean() if self.end_date and self.start_date > self.end_date: raise ValidationError({ 'end_date': 'End date must come after the start date', }) if self.end_date: conflict_query = ( Q(start_date__range=[self.start_date, self.end_date]) | Q(end_date__range=[self.start_date, self.end_date]) | Q(start_date__lte=self.start_date, end_date__gte=self.end_date)) else: conflict_query = (Q(start_date__lte=self.start_date, end_date__isnull=True) | Q(end_date__gte=self.start_date)) if not self.id and hasattr(self, 'parent_page'): # Check if the create hook has added the parent page, we aren't an object yet. # Ensures we can access related objects during the clean phase instead of save. base_query = RoundBase.objects.child_of( self.parent_page[self.__class__][self.title]) else: # don't need parent page, we are an actual object now. base_query = RoundBase.objects.sibling_of(self) conflicting_rounds = base_query.filter(conflict_query).exclude( id=self.id) if conflicting_rounds.exists(): error_message = mark_safe( 'Overlaps with the following rounds:<br> {}'. format('<br>'.join([ f'<a href="{admin_url(round)}">{round.title}</a>: {round.start_date} - {round.end_date}' for round in conflicting_rounds ]))) error = { 'start_date': error_message, } if self.end_date: error['end_date'] = error_message raise ValidationError(error) def serve(self, request): if hasattr(request, 'is_preview') or hasattr(request, 'show_round'): return super().serve(request) # We hide the round as only the open round is used which is displayed through the # fund page raise Http404()
class ThreeLegStandingsEntry(models.Model): page = ParentalKey('ThreeLegStandingsPage', related_name='results', on_delete=models.CASCADE) team_name = models.CharField(max_length=50) team_is_novice = models.BooleanField(default=False) leg_1_score = models.IntegerField(blank=True, default=0) leg_1_hits = models.IntegerField(blank=True, default=0) leg_1_golds = models.IntegerField(blank=True, default=0) leg_2_score = models.IntegerField(blank=True, default=0) leg_2_hits = models.IntegerField(blank=True, default=0) leg_2_golds = models.IntegerField(blank=True, default=0) leg_3_score = models.IntegerField(blank=True, default=0) leg_3_hits = models.IntegerField(blank=True, default=0) leg_3_golds = models.IntegerField(blank=True, default=0) champs_score = models.IntegerField(blank=True, default=0) champs_hits = models.IntegerField(blank=True, default=0) champs_golds = models.IntegerField(blank=True, default=0) @property def leg_1(self): return self.leg_1_score, self.leg_1_hits, self.leg_1_golds @property def leg_2(self): return self.leg_2_score, self.leg_2_hits, self.leg_2_golds @property def leg_3(self): return self.leg_3_score, self.leg_3_hits, self.leg_3_golds @property def champs(self): return self.champs_score, self.champs_hits, self.champs_golds @property def results(self): return [self.leg_1, self.leg_2, self.leg_3, self.champs] @property def is_empty(self): return not bool(list(filter(lambda x: x != (0, 0, 0), self.results))) @property def aggregate(self): return functools.reduce( lambda acc, new: (acc[0] + new[0], acc[1] + new[1], acc[2] + new[2]), self.results) panels = [ FieldPanel('team_name', classname='title'), FieldPanel('team_is_novice'), FieldRowPanel([ FieldPanel('leg_1_score'), FieldPanel('leg_1_hits'), FieldPanel('leg_1_golds'), ]), FieldRowPanel([ FieldPanel('leg_2_score'), FieldPanel('leg_2_hits'), FieldPanel('leg_2_golds'), ]), FieldRowPanel([ FieldPanel('leg_3_score'), FieldPanel('leg_3_hits'), FieldPanel('leg_3_golds'), ]), FieldRowPanel([ FieldPanel('champs_score'), FieldPanel('champs_hits'), FieldPanel('champs_golds'), ]), ]
class InvestHomePage(ExclusivePageMixin, ServiceHomepageMixin, BaseInvestPage): slug_identity = slugs.INVEST_HOME_PAGE view_path = '' breadcrumbs_label = models.CharField(max_length=50) heading = models.CharField(max_length=255) sub_heading = models.CharField(max_length=255) hero_call_to_action_text = models.CharField(max_length=255, blank=True) hero_call_to_action_url = models.CharField(max_length=255, blank=True) hero_image = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+') benefits_section_title = models.CharField(max_length=255, blank=True) benefits_section_intro = models.TextField(max_length=255, blank=True) benefits_section_content = MarkdownField(blank=True) benefits_section_img = models.ForeignKey( 'wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', verbose_name="Benefits section image") eu_exit_section_title = models.CharField( max_length=255, blank=True, verbose_name="EU exit section title") eu_exit_section_content = MarkdownField( blank=True, verbose_name="EU exit section content") eu_exit_section_call_to_action_text = models.CharField( max_length=255, blank=True, verbose_name="EU exit section button text") eu_exit_section_call_to_action_url = models.CharField( max_length=255, blank=True, verbose_name="EU exit section button url") eu_exit_section_img = models.ForeignKey( 'wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', verbose_name="EU exit section image") sector_title = models.TextField(default="Discover UK Industries", max_length=255, blank=True) sector_button_text = models.TextField(default="See more industries", max_length=255, blank=True) sector_button_url = models.CharField(max_length=255, blank=True) sector_intro = models.TextField(max_length=255, blank=True) hpo_title = models.CharField( max_length=255, verbose_name="High potential opportunity section title", blank=True) hpo_intro = models.TextField( max_length=255, blank=True, verbose_name="High potential opportunity section intro") capital_invest_section_title = models.CharField(max_length=255, blank=True) capital_invest_section_content = MarkdownField(blank=True) capital_invest_section_image = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+') setup_guide_title = models.CharField( default='Set up an overseas business in the UK', max_length=255, blank=True) setup_guide_lead_in = models.TextField(blank=True, null=True) setup_guide_content = MarkdownField(blank=True) setup_guide_img = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', verbose_name="Setup guide image") setup_guide_call_to_action_url = models.CharField(max_length=255, blank=True) isd_section_image = models.ForeignKey( 'wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Investment Support Directory section image') isd_section_title = models.CharField( max_length=255, blank=True, verbose_name='Investment Support Directory section title') isd_section_text = MarkdownField( max_length=255, blank=True, verbose_name='Investment Support Directory section text') featured_card_one_image = models.ForeignKey( 'wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', ) featured_card_one_title = models.CharField(blank=True, max_length=255) featured_card_one_summary = MarkdownField(blank=True) featured_card_one_cta_link = models.CharField(max_length=255, blank=True) featured_card_two_image = models.ForeignKey( 'wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+', ) featured_card_two_title = models.CharField( max_length=255, blank=True, ) featured_card_two_summary = MarkdownField( max_length=255, blank=True, ) featured_card_two_cta_link = models.CharField(max_length=255, blank=True) featured_card_three_image = models.ForeignKey('wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, related_name='+') featured_card_three_title = models.CharField(max_length=255, blank=True) featured_card_three_summary = MarkdownField(blank=True) featured_card_three_cta_link = models.CharField(max_length=255, blank=True) how_we_help_title = models.CharField(default='How we help', max_length=255, blank=True) how_we_help_lead_in = models.TextField(blank=True, null=True) # how we help how_we_help_text_one = models.CharField(max_length=255, blank=True) how_we_help_icon_one = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') how_we_help_text_two = models.CharField(max_length=255, blank=True) how_we_help_icon_two = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') how_we_help_text_three = models.CharField(max_length=255, blank=True) how_we_help_icon_three = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') how_we_help_text_four = models.CharField(max_length=255, blank=True) how_we_help_icon_four = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') how_we_help_text_five = models.CharField(max_length=255, blank=True) how_we_help_icon_five = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') how_we_help_text_six = models.CharField(max_length=255, blank=True) contact_section_title = models.CharField(max_length=255, blank=True) contact_section_content = models.TextField(max_length=255, blank=True) contact_section_call_to_action_text = models.CharField(max_length=255, blank=True) contact_section_call_to_action_url = models.CharField(max_length=255, blank=True) image_panels = [ ImageChooserPanel('hero_image'), ] content_panels = [ MultiFieldPanel( heading='Hero', classname='collapsible', children=[ FieldPanel('breadcrumbs_label'), FieldPanel('heading'), FieldPanel('sub_heading'), FieldPanel('hero_call_to_action_text'), FieldPanel('hero_call_to_action_url'), ], ), MultiFieldPanel( heading='Benefits section', classname='collapsible', children=[ HelpPanel('Required fields for section to show: ' 'Benefits Section Title, Benefits Section Content'), FieldPanel('benefits_section_title'), FieldPanel('benefits_section_intro'), FieldPanel('benefits_section_content'), ImageChooserPanel('benefits_section_img'), ], ), MultiFieldPanel( heading='EU Exit section', classname='collapsible collapsed', children=[ FieldPanel('eu_exit_section_title'), FieldPanel('eu_exit_section_content'), FieldPanel('eu_exit_section_call_to_action_text'), FieldPanel('eu_exit_section_call_to_action_url'), ImageChooserPanel('eu_exit_section_img'), ], ), MultiFieldPanel( heading='Old featured card links', classname='collapsible collapsed', children=[ FieldRowPanel([ MultiFieldPanel([ ImageChooserPanel('setup_guide_img'), FieldPanel('setup_guide_title'), FieldPanel('setup_guide_content'), FieldPanel('setup_guide_call_to_action_url'), ], ), MultiFieldPanel([ ImageChooserPanel('isd_section_image'), FieldPanel('isd_section_title'), FieldPanel('isd_section_text') ], ), MultiFieldPanel([ ImageChooserPanel('capital_invest_section_image'), FieldPanel('capital_invest_section_title'), FieldPanel('capital_invest_section_content'), ]), ]), ], ), MultiFieldPanel( heading='Featured card links ', classname='collapsible', children=[ HelpPanel('Required fields for section to show: ' 'All images, titles and summaries'), FieldRowPanel([ MultiFieldPanel([ ImageChooserPanel('featured_card_one_image'), FieldPanel('featured_card_one_title'), FieldPanel('featured_card_one_summary'), FieldPanel('featured_card_one_cta_link'), ], ), MultiFieldPanel([ ImageChooserPanel('featured_card_two_image'), FieldPanel('featured_card_two_title'), FieldPanel('featured_card_two_summary'), FieldPanel('featured_card_two_cta_link'), ], ), MultiFieldPanel([ ImageChooserPanel('featured_card_three_image'), FieldPanel('featured_card_three_title'), FieldPanel('featured_card_three_summary'), FieldPanel('featured_card_three_cta_link'), ]), ]), ], ), MultiFieldPanel( heading='Industries section', children=[ HelpPanel('Required fields for section to show: ' 'Sector Title, Sector Content'), FieldPanel('sector_title'), FieldPanel('sector_intro'), FieldPanel('sector_button_text'), FieldPanel('sector_button_url'), ], ), MultiFieldPanel( heading='High Potential Opportunities', children=[ HelpPanel('Required fields for section to show: ' 'HPO title, 1 HPO in active language'), FieldPanel('hpo_title'), FieldPanel('hpo_intro') ], ), MultiFieldPanel( heading='How we help section', classname='collapsible', children=[ HelpPanel('Required fields for section to show: ' 'How We Help Title, How We Help Lead In'), FieldPanel('how_we_help_title'), FieldPanel('how_we_help_lead_in'), HelpPanel('Each icon requires the corresponding text to ' 'show on the page'), FieldRowPanel([ MultiFieldPanel([ FieldPanel('how_we_help_text_one'), ImageChooserPanel('how_we_help_icon_one') ], ), MultiFieldPanel([ FieldPanel('how_we_help_text_two'), ImageChooserPanel('how_we_help_icon_two') ], ), MultiFieldPanel([ FieldPanel('how_we_help_text_three'), ImageChooserPanel('how_we_help_icon_three') ], ), ], ), FieldRowPanel([ MultiFieldPanel([ FieldPanel('how_we_help_text_four'), ImageChooserPanel('how_we_help_icon_four') ], ), MultiFieldPanel([ FieldPanel('how_we_help_text_five'), ImageChooserPanel('how_we_help_icon_five') ], ) ], ), ], ), MultiFieldPanel( heading='Contact Section', classname='collapsible', children=[ HelpPanel('Required fields for section to show: ' 'Contact Title, Contact Content'), FieldPanel('contact_section_title'), FieldPanel('contact_section_content'), HelpPanel('Cta\'s require both text and a link to show ' 'on page. '), FieldPanel('contact_section_call_to_action_text'), FieldPanel('contact_section_call_to_action_url'), ], ), SearchEngineOptimisationPanel() ] settings_panels = [ FieldPanel('title_en_gb'), FieldPanel('slug'), ] edit_handler = make_translated_interface(content_panels=content_panels, settings_panels=settings_panels, other_panels=[ ObjectList(image_panels, heading='Images'), ])
class Team(models.Model): """This class represents a working group within UTN""" class Meta: verbose_name = _('Team') verbose_name_plural = _('Teams') default_permissions = () # ---- General Information ------ name_en = models.CharField( max_length=255, verbose_name=_('English team name'), help_text=_('Enter the name of the team'), blank=False, ) name_sv = models.CharField( max_length=255, verbose_name=_('Swedish team name'), help_text=_('Enter the name of the team'), blank=False, ) name = TranslatedField('name_en', 'name_sv') logo = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') description_en = models.TextField( verbose_name=_('English team description'), help_text=_('Enter a description of the team'), blank=True, ) description_sv = models.TextField( verbose_name=_('Swedish team description'), help_text=_('Enter a description of the team'), blank=True, ) description = TranslatedField('description_en', 'description_sv') def __str__(self) -> str: return '{}'.format(self.name) @property def members(self): return get_user_model().objects.filter( application__position__role__teams__pk=self.pk, application__position__term_from__lte=date.today(), application__position__term_to__gte=date.today(), application__status='appointed', ) @property def manual_members(self): members = self.members.values('pk') return get_user_model().objects.filter(groups=self.group).exclude( pk__in=members) # ------ Administrator settings ------ panels = [ MultiFieldPanel([ FieldRowPanel([ FieldPanel('name_en'), FieldPanel('name_sv'), ]), ImageChooserPanel('logo'), FieldPanel('description_en'), FieldPanel('description_sv'), ]) ]
class Homepage(FoundationMetadataPageMixin, Page): hero_headline = models.CharField( max_length=140, help_text='Hero story headline', blank=True, ) hero_story_description = RichTextField(features=[ 'bold', 'italic', 'link', ]) hero_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='hero_image') hero_button_text = models.CharField(max_length=50, blank=True) hero_button_url = models.URLField(blank=True) content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('hero_headline'), FieldPanel('hero_story_description'), FieldRowPanel([ FieldPanel('hero_button_text'), FieldPanel('hero_button_url'), ], ), ImageChooserPanel('hero_image'), ], heading='hero', classname='collapsible'), InlinePanel('featured_blogs', label='Blogs', max_num=4), InlinePanel('featured_highlights', label='Highlights', max_num=5), ] subpage_types = [ 'BanneredCampaignPage', 'IndexPage', 'InitiativesPage', 'MiniSiteNameSpace', 'NewsPage', 'OpportunityPage', 'ParticipatePage', 'ParticipatePage2', 'PeoplePage', 'PrimaryPage', 'RedirectingPage', 'Styleguide', ] def get_context(self, request): # We need to expose MEDIA_URL so that the s3 images will show up properly # due to our custom image upload approach pre-wagtail context = super().get_context(request) context['MEDIA_URL'] = settings.MEDIA_URL context['menu_root'] = self context['menu_items'] = self.get_children().live().in_menu() return context
class AbstractAttendeeRelation(Orderable): class Meta: abstract = True attendee_name_en = models.CharField( max_length=512, verbose_name=_('name of the attendee type (english)'), blank=True, null=True) attendee_name_de = models.CharField( max_length=512, verbose_name=_('name of the attendee type (german)'), blank=True, null=True) attendee_name = TranslatedField('attendee_name') description_en = RichTextField( blank=True, features=['bold', 'italic', 'link', 'ol', 'ul'], verbose_name=_('description of the attendee type (english)')) description_de = RichTextField( blank=True, features=['bold', 'italic', 'link', 'ol', 'ul'], verbose_name=_('description of the attendee type (german)')) description = TranslatedField('description') attendee = models.CharField(max_length=16, verbose_name=_('required form data')) waitlist = models.BooleanField(default=False, verbose_name=_('waitlist')) price = models.DecimalField(max_digits=7, decimal_places=2, default=0, blank=True, null=True, verbose_name=_('price')) max_attendees = models.IntegerField(blank=True, null=True, verbose_name=_('max attendees')) panels = [ FieldPanel('attendee_name_en'), FieldPanel('attendee_name_de'), FieldPanel('attendee', widget=AttendeeSelectWidget()), FieldPanel('description_en'), FieldPanel('description_de'), FieldPanel('price'), FieldRowPanel([ FieldPanel('max_attendees'), FieldPanel('waitlist'), ]) ] def get_attendee_class(self): if not self.attendee: return None return get_attendee_class(self.attendee)
class OrbitalMechanicsMixin(models.Model): """ A mixin for anything that orbits something else (e.g. a planet or a moon). This mixin assumes its direct parent has mass. """ semi_major_axis = MultiQuantityField(units=(ureg.au, ureg.km)) eccentricity = models.DecimalField(max_digits=5, decimal_places=5) inclination = MultiQuantityField(units=(ureg.degree, ureg.radian)) longitude_of_the_ascending_node = MultiQuantityField(units=(ureg.degree, ureg.radian)) argument_of_periapsis = MultiQuantityField(units=(ureg.degree, ureg.radian)) # Local motion rotational_period = MultiQuantityField(units=(ureg.hour, ureg.day)) obliquity = MultiQuantityField(units=(ureg.degree, ureg.radian)) precessional_period = MultiQuantityField(units=(ureg.year,)) @property def orbited_object(self): return self.get_parent().specific @property def periapsis(self): return self.semi_major_axis * (1 - float(self.eccentricity)) @property def apoapsis(self): return self.semi_major_axis * (1+ float(self.eccentricity)) @property def orbital_period(self): return (((4 * math.pi**2) / (GRAVITATIONAL_CONSTANT * self.orbited_object.mass)) * self.semi_major_axis**3)**0.5 @property def longitude_of_periapsis(self): return self.longitude_of_the_ascending_node + self.argument_of_periapsis @property def day_length(self): return self.rotational_period @property def local_days_in_year(self): return self.orbital_period / self.rotational_period @property def tropics(self): return self.obliquity @property def polar_circles(self): return Q_('90 degree') - self.obliquity content_panels = [MultiFieldPanel([ FieldRowPanel([ FieldPanel('semi_major_axis'), FieldPanel('eccentricity'), ]), FieldRowPanel([ FieldPanel('inclination'), FieldPanel('longitude_of_the_ascending_node'), FieldPanel('argument_of_periapsis'), ]), FieldRowPanel([ FieldPanel('rotational_period'), FieldPanel('obliquity'), FieldPanel('precessional_period'), ]), ], 'Orbital properties')] class Meta: abstract = True
class Course(RoutablePageMixin, TranslatedPage, BodyMixin): class Meta: verbose_name = _('Course date') parent_page_types = [CourseInformationPage] start = models.DateField(verbose_name=_('start date')) end = models.DateField(blank=True, null=True, verbose_name=_('end date'), help_text=_('leave empty for a one-day course')) overrule_parent = models.BooleanField( default=False, verbose_name=_('Overrule standard settings')) register_via_website = models.BooleanField( default=False, verbose_name=_('Registration via website')) share_data_via_website = models.BooleanField( default=False, verbose_name=_('Share data via website')) max_attendees = models.IntegerField(blank=True, null=True, default=None, verbose_name=_('Max attendees')) script = StreamField( [ ('chapter', blocks.CharBlock( classname="full title", required=True, label='Kapitel')), ('section', blocks.CharBlock(required=True, label='Unterkapitel')), ('file', DocumentChooserBlock(label="Datei mit Folien")), ], verbose_name=_('Script for the course'), help_text=_('This allows to combine several PDFs to a single one'), null=True, blank=True) script_title = models.CharField(max_length=512, blank=True) script_subtitle1 = models.CharField(max_length=512, blank=True) script_subtitle2 = models.CharField(max_length=512, blank=True) script_date = models.CharField(max_length=2, choices=( ('d', 'german'), ('e', 'english'), ('n', 'no date'), ), default='n') fill_automatically = models.BooleanField( default=False, help_text=_('Fill with attendees from waitlist automatically?')) content_panels = [ FieldRowPanel([FieldPanel('start'), FieldPanel('end')]), StreamFieldPanel('body_en'), StreamFieldPanel('body_de'), ] settings_panels = [ FieldPanel('overrule_parent'), MultiFieldPanel([ FieldRowPanel([ FieldPanel('register_via_website'), FieldPanel('share_data_via_website'), ]), ], heading=_('Registration options')), FieldPanel('max_attendees'), InlinePanel('attendee_types', label=_('allowed attendees')) ] script_panel = [ FieldPanel('script_title'), FieldPanel('script_subtitle1'), FieldPanel('script_subtitle2'), FieldPanel('script_date'), StreamFieldPanel('script') ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading=_('Date and info')), ObjectList(settings_panels, heading=_('Overrule parent settings')), ObjectList(script_panel, heading=_('Script')), ]) def clean(self): self.title = '{}–{}'.format(self.start, self.end) self.title_de = self.title if not self.slug: self.slug = self._get_autogenerated_slug(slugify(self.title)) def _get_me_or_parent(self): if self.overrule_parent: return self else: return self.get_parent().specific @property def is_bookable(self): return self._get_me_or_parent().register_via_website def started_in_past(self): return self.start <= datetime.date.today() def get_attendee_types(self): return self._get_me_or_parent().attendee_types def get_max_attendees(self): return self._get_me_or_parent().max_attendees @property def has_data_sharing(self): return self._get_me_or_parent().share_data_via_website def get_price(self, attendee): for at_rel in self.get_attendee_types().all(): if at_rel.get_attendee_class() == attendee: return at_rel.price return 0 def has_waitlist_for(self, attendee): return self.get_attendee_types().filter(attendee=attendee.identifier, waitlist=True).count() > 0 @property def registered_attendees_stats(self): ''' Returns an overview of the the registered attendees ''' attendees = {} for atype in self.get_attendee_types().all(): Klass = atype.get_attendee_class() if not Klass: continue try: disp = Klass.display_name_plural except AttributeError: disp = Klass.display_name # The total number of attendees for one attendee-type # is the sum of # # a) those who are validated qs = Klass.objects.filter(related_course=self) num = qs.filter(is_validated=True).count() # b) those who are not yet validated, but the # mail asking for validation has been sent within the last week minus_one_week = datetime.date.today() - relativedelta(weeks=1) num = num + qs.filter( is_validated=False, validation_mail_sent=True, validation_mail_sent_at__gte=minus_one_week).count() # c) People from the waitlist that have been asked to # attend this course, but have not yet answered and the # mail was sent within the last week num = num + Klass.objects.filter( waitlist_course=self, add2course_mail_sent=True, add2course_mail_sent_at__gte=minus_one_week).count() # This is just in case that two or more attendee types have the # same display name. Should not happen! It might be better # to solve this by something like: # { 'unique' : ('display', num) } # # (see method get_free_slots below) c = 0 while disp in attendees: c += 1 disp = "{}_{}".format(disp, c) attendees[disp] = num return attendees def get_free_slots(self, Attendee): print('Attendee is: {}'.format(Attendee)) stats = self.registered_attendees_stats # we need idx to be a unique index. try: idx = Attendee.display_name_plural except AttributeError: idx = Attendee.display_name try: n_attendees = stats[idx] except KeyError: # if idx is not in stats, this Attendee type is not allowed # for this course. Thus, free slots is 0 return 0 # get max_attendees for this attendee-type, if exists limit_type = (self.get_attendee_types().get( attendee=Attendee.identifier).max_attendees) if limit_type is not None: attendee_free = limit_type - n_attendees if attendee_free < 0: # might happen during testing or if someone adds an attendee # manually attendee_free = 0 else: attendee_free = None limit_total = self.get_max_attendees() # compute total number of attendees total_attendees = 0 for k, v in stats.items(): total_attendees = total_attendees + v total_free = limit_total - total_attendees if total_free < 0: # see above... total_free = 0 # Very import to test for None, since 0 is interpreted as False... if attendee_free is not None: return min(total_free, attendee_free) else: return total_free def get_n_attendees(self): c = 0 for k, v in self.registered_attendees_stats.items(): c = c + v return c @property def is_fully_booked(self): try: return self.get_n_attendees() >= self.get_max_attendees() except TypeError: return False def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) # test whether there are any free slots in this course n_attendees = 0 for k, v in self.registered_attendees_stats.items(): n_attendees = n_attendees + v # is there any attendee_type with a waitlist? waitlist = self.get_attendee_types().all().filter( waitlist=True).count() > 0 if self.get_max_attendees(): if not waitlist and self.get_max_attendees() <= n_attendees: context['fully_booked'] = True else: context['fully_booked'] = False # test if we have a waitlist for any allowed attendee type # and no slots left for the type... free_courses = {} for a2c_rel in self.get_attendee_types().all().filter(waitlist=True): if self.get_free_slots(a2c_rel.get_attendee_class()) == 0: # Do we have another course, not fully booked, for that attendee type? upcoming_courses = self.get_parent( ).specific.get_upcoming_courses().exclude(id=self.id) for uc in upcoming_courses: if uc.specific.get_free_slots( a2c_rel.get_attendee_class()) > 0: if not a2c_rel.get_attendee_class( ).display_name_plural in free_courses.keys(): free_courses[a2c_rel.get_attendee_class(). display_name_plural] = [] free_courses[a2c_rel.get_attendee_class(). display_name_plural].append(uc) if free_courses: context['show_waitlist_info'] = True context['free_courses'] = free_courses return context @route(r'^register/(\w+)/$', name="register") @route(r'^register/(\w+)/(\d+)/$', name="register") def register_for_course(self, request, attendee, form_count=0): # implements a multi-step form for registration. # The forms are provided by `forms` list of the corresponding attendee class. # get the form count form_count = int(form_count) # if a form has been filled is stored in request.session.form<number> # Avoid that a form is shown before the previous has been filled: if form_count > 0: if not request.session.get('form{}_filled'.format(form_count - 1)): return redirect(self.url + self.reverse_subpage( 'register', args=[attendee, form_count - 1])) # get the attende by the "register_attendee" mechanism and raise a 404 if not known Attendee = get_attendee_class(attendee) if not Attendee: raise Http404 # IF THE COURSE IS IN THE PAST, RAISE A 404 if self.start <= datetime.date.today(): raise Http404 # @TODO: Test if attendee is allowed for this course # collect all form classes... forms = [] for f in Attendee.forms: module_name, class_name = f.rsplit(".", 1) module = importlib.import_module(module_name) FormKlass = getattr(module, class_name) forms.append(FormKlass) # Check if there are earlier courses that have a waitlist for this Attendee class waitlist_available = False previous_courses = self.get_parent().specific.get_upcoming_courses( ).filter(start__lt=self.start).order_by('-start') for pc in previous_courses.all(): if pc.get_free_slots(Attendee) == 0 and pc.get_attendee_types( ).filter(waitlist=True, attendee=Attendee.identifier).count() > 0: waitlist_available = True waitlist_course = pc break # and if there is one, add the respective form class in the second last position # but only, if this regsitration is for the course, and not for the waitlist of this course has_waitlist = self.has_waitlist_for(Attendee) free_slots = self.get_free_slots(Attendee) uses_waitlist = has_waitlist and free_slots < 1 if uses_waitlist: waitlist_available = False if waitlist_available: from .forms import WaitlistForm last_form = forms[-1] forms[-1] = WaitlistForm forms.append(last_form) # ... and get the current one try: CurrentFormKlass = forms[form_count] except KeyError: raise Http404 kwargs = {} # there might be some values passed from the previous form provided_values = request.session.get( 'provided_values{}'.format(form_count - 1), None) if provided_values: kwargs['provided_values'] = provided_values print('Form count is: {}'.format(form_count)) if waitlist_available and form_count == len(forms) - 2: kwargs['course'] = pc # now the usual stuff if request.method == 'GET': # @TODO. # This code might be executed if someone clicks on the "step indicator" # try to fill it with the already collected data... if request.session.get('form{}_filled'.format(form_count), None): data_json = request.session.get('cleaned_data', None) if data_json: data = json.loads(data_json) else: data = {} kwargs['initial'] = data else: if provided_values: kwargs['initial'] = provided_values form = CurrentFormKlass(**kwargs) if request.method == 'POST': form = CurrentFormKlass(request.POST, **kwargs) if form.is_valid(): if hasattr(form, 'is_validated'): cleaned_data = {'is_validated': True} else: cleaned_data = {} # Get the json for the data from the session data_json = request.session.get('cleaned_data', None) if data_json: cleaned_data.update(json.loads(data_json)) if issubclass(CurrentFormKlass, ModelForm) or (waitlist_available and form_count == len(forms) - 2): for key, value in form.cleaned_data.items(): cleaned_data[key] = value else: try: request.session['provided_values{}'.format( form_count)] = form.provided_values except AttributeError: request.session['provided_values{}'.format( form_count)] = {} request.session['form{}_filled'.format(form_count)] = True request.session['cleaned_data'] = json.dumps(cleaned_data, cls=PHJsonEncoder) request.session.modified = True if form_count < len(forms) - 1: messages.info( request, format_lazy( 'Your data for step {} has been saved. Please proceed with step {}.', form_count + 1, form_count + 2)) return redirect(self.url + self.reverse_subpage( 'register', args=[attendee, form_count + 1])) else: instance = form.save(commit=False) for key, value in cleaned_data.items(): if key == 'waitlist' and waitlist_available: setattr(instance, 'waitlist_course', pc) else: setattr(instance, key, value) # Attendees that have to pay have the amount of the fee and the # amount already payed in the database. We need to provide values here... if hasattr(instance, 'amount'): instance.amount = self.get_price(Attendee) if hasattr(instance, 'payed'): instance.payed = False instance.related_course = self if self.get_free_slots(Attendee) > 0: instance.save() if not instance.is_validated: return redirect(self.url + self.reverse_subpage( 'validation_mail', args=[instance.courseattendee_ptr_id])) else: return redirect(self.url + self.reverse_subpage( 'success', args=[instance.courseattendee_ptr_id])) elif has_waitlist: instance.waitlist_course = self instance.related_course = None instance.save() messages.info(request, _('You were put on the waiting list.')) if not instance.is_validated: return redirect(self.url + self.reverse_subpage( 'validation_mail', args=[instance.courseattendee_ptr_id])) else: return redirect(self.url + self.reverse_subpage( 'success', args=[instance.courseattendee_ptr_id])) else: messages.error( request, _('The course is fully booked. You have not been registered.' )) messages.info(request, _('We have not stored your data.')) try: del request.session['cleaned_data'] except KeyError: pass return redirect(self.url) price = self.get_price(Attendee) show_price_hint = price > 0 and form_count == len(forms) - 1 return TemplateResponse( request, 'courses/register.html', { 'attendee': attendee, 'attendee_name': Attendee.display_name, 'forms': forms, 'current_form': form_count, 'form': form, 'page': self, 'price': price, 'show_price_hint': show_price_hint, 'free_slots': free_slots, 'has_waitlist': has_waitlist }) @route(r'^success/(\d+)/$', name="success") def success(self, request, attendee_id): attendee = get_object_or_404(CourseAttendee, id=attendee_id) if attendee.confirmation_mail_sent: raise Http404 mailtext = EMailText.objects.get( identifier='courses.registration.confirmation') mailtext.send( attendee.email, { 'attendee': attendee, 'course': self, 'waitlist': attendee.waitlist_course == self }) self.send_invoice(attendee) try: del request.session['cleaned_data'] except KeyError: pass return TemplateResponse(request, 'courses/success.html', {'page': self}) @route(r'^validation_required/(\d+)/$', name="validation_mail") def validation_mail(self, request, attendee_id): attendee = get_object_or_404(CourseAttendee, id=attendee_id) validation = CourseParticipationValidation() validation.attendee = attendee validation.save() now = datetime.datetime.now() attendee.validation_mail_sent = True attendee.validation_mail_sent_at = now attendee.save() valid_until = now + relativedelta(weeks=+1) if valid_until.date() >= self.start: valid_until = self.start + relativedelta(days=-2) mailtext = EMailText.objects.get( identifier='courses.registration.validation') mailtext.send( attendee.email, { 'attendee': attendee, 'course': self, 'validation_url': request.build_absolute_uri( reverse('manage-courses:validate_course_registration', kwargs={'uuid': validation.uuid})), 'validation': validation, 'valid_until': valid_until }) try: del request.session['cleaned_data'] except KeyError: pass return TemplateResponse(request, 'courses/validation_mail.html', { 'page': self, 'valid_until': valid_until, 'attendee': attendee }) def send_invoice(self, attendee): # get the specific course attendee object for Kls in ATTENDEE_TYPES: try: s_att = Kls.objects.get(courseattendee_ptr_id=attendee.id) break except Kls.DoesNotExist: pass attendee = s_att try: if attendee.amount == 0: return except AttributeError: return parent = self.get_parent().specific staff_mails = [ staff.person.mail for staff in parent.contact_persons.all() ] pdffile = RUBIONCourseInvoice( attendee, parent.contact_persons.first().person, ) pdffilename = pdffile.write2file() mailtext = EMailText.objects.get( identifier='courses.staff.invoice_copy') mailtext.send(staff_mails, { 'course': self, 'attendee': attendee, 'invoice_was_sent': False }, attachements=[pdffilename], lang='de') # Should mails be sent automatically or manually? # I guess we said manually, at least in the beginning. #MAILTEXT = EMailText.objects.get(identifier = 'courses.attendee.invoice' ) #mailtext.send( # attendee.email, # { # 'attendee': attendee, # 'course' : self # } #) def get_data_sharing_page(self): if DataSharingPage.objects.child_of(self).exists(): return DataSharingPage.objects.child_of(self).first() else: return None def save(self, *args, **kwargs): super(Course, self).save(*args, **kwargs) if self.has_data_sharing: if not self.get_data_sharing_page(): dsp = DataSharingPage() dsp.clean() self.add_child(dsp) characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_*?!$&" pvr = PageViewRestriction( page=dsp, restriction_type=PageViewRestriction.PASSWORD, password="".join( random.choice(characters) for __ in range(10))) pvr.save()
class BlogDetailPage(Page): """Blog Detail Page""" template = "blog/blog-detail.html" parent_page_types = ["blog.BlogIndexPage"] """ - upvotes * - times read * - read time * """ author = models.ForeignKey("blog.BlogAuthor", on_delete=models.PROTECT, related_name="+", help_text="Select the author of this blog post") image = models.ForeignKey( "wagtailimages.Image", null=True, blank=False, related_name="+", on_delete=models.SET_NULL, help_text="Select an image to use on the post's page") date_published = models.DateTimeField(auto_now_add=True) quote = models.TextField(_("A Programming Quote"), help_text="Enter some programming quote", blank=False, null=False) category = models.ForeignKey( "blog.BlogCategory", on_delete=models.PROTECT, related_name="+", help_text="Select the category of this blog post") tags = ClusterTaggableManager(through=BlogPageTag, blank=True) content = StreamField([ ("heading", blocks.CharBlock()), ("text", blocks.TextBlock()), ("paragraph", blocks.RichTextBlock(template="blocks/rich-text-block.html", features=[ 'h1', 'h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'hr', 'link', 'image', 'code', 'blockquote' ])), ("code", CodeBlock()), ("image", ImageChooserBlock()), ]) def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context["posts_from_author"] = BlogDetailPage.objects.live().filter( author__full_name=self.author.full_name).exclude( title=self.title)[:5] return context content_panels = Page.content_panels + [ ImageChooserPanel("image"), MultiFieldPanel([ FieldRowPanel([ SnippetChooserPanel("author"), SnippetChooserPanel("category") ]), ], heading="Author and Category"), FieldPanel("tags"), FieldPanel("quote"), MultiFieldPanel([ StreamFieldPanel("content"), ], heading="Blog Content", classname="collapse collapsed"), MultiFieldPanel([ InlinePanel( "related_posts", max_num=10, min_num=1, heading="Related Post") ], heading="Related Blog Posts") ]
class CourseInformationPage(TranslatedPage, MandatoryIntroductionMixin, BodyMixin, ChildMixin): ''' A general model for a course. ''' subpage_types = ['courses.Course'] parent_page_types = ['courses.ListOfCoursesPage'] class Meta: verbose_name = _('Definition of a course') # ID in the courses register course_id = models.CharField(max_length=64, blank=True) register_via_website = models.BooleanField(default=False) share_data_via_website = models.BooleanField(default=False) max_attendees = models.IntegerField(blank=True, null=True, default=None) information_list = models.BooleanField( default=False, help_text= _('Is there a list that stores people intereted in this course if no course date is available?' )) content_panels_de = [ FieldPanel('title_de'), StreamFieldPanel('introduction_de'), StreamFieldPanel('body_de'), ] content_panels_en = [ FieldPanel('title'), StreamFieldPanel('introduction_en'), StreamFieldPanel('body_en'), ] additional_information_panel = [ FieldPanel('course_id'), InlinePanel('contact_persons', label=_('Contact persons')) ] settings_panel = [ FieldPanel('information_list'), MultiFieldPanel([ FieldRowPanel([ FieldPanel('register_via_website'), FieldPanel('share_data_via_website'), ]), ], heading=_('Registration options')), FieldPanel('max_attendees'), InlinePanel('attendee_types', label=_('allowed attendees')) ] edit_handler = TabbedInterface([ ObjectList(content_panels_de, heading=_('Content (de)')), ObjectList(content_panels_en, heading=_('Content (en)')), ObjectList(additional_information_panel, heading=_('Additional information')), ObjectList(settings_panel, heading=_('Settings')), ]) def get_upcoming_courses(self): children = Course.objects.live().child_of(self).filter( start__gt=datetime.date.today()).order_by('start') return children def get_admin_display_title(self): return self.title_trans
class TestFieldRowPanel(TestCase): def setUp(self): self.request = RequestFactory().get('/') user = AnonymousUser() # technically, Anonymous users cannot access the admin self.request.user = user self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', date_from=date(2014, 7, 20), date_to=date(2014, 7, 21)) self.dates_panel = FieldRowPanel([ FieldPanel('date_from', classname='col4'), FieldPanel('date_to', classname='coltwo'), ]).bind_to(model=EventPage, request=self.request) def test_render_as_object(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-22'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to( instance=self.event, form=form, ) result = field_panel.render_as_object() # check that the populated form field is included self.assertIn('value="2014-07-22"', result) # there should be no errors on this field self.assertNotIn('<p class="error-message">', result) def test_render_as_field(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-22'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to( instance=self.event, form=form, ) result = field_panel.render_as_field() # check that label is output in the 'field' style self.assertIn('<label for="id_date_to">End date:</label>', result) self.assertNotIn('<legend>End date</legend>', result) # check that help text is included self.assertIn('Not required if event is on a single day', result) # check that the populated form field is included self.assertIn('value="2014-07-22"', result) # there should be no errors on this field self.assertNotIn('<p class="error-message">', result) def test_error_message_is_rendered(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-33'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to( instance=self.event, form=form, ) result = field_panel.render_as_field() self.assertIn('<p class="error-message">', result) self.assertIn('<span>Enter a valid date.</span>', result) def test_add_col_when_wrong_in_panel_def(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-33'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to( instance=self.event, form=form, ) result = field_panel.render_as_field() self.assertIn('<li class="field-col coltwo col6', result) def test_added_col_doesnt_change_siblings(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-33'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to( instance=self.event, form=form, ) result = field_panel.render_as_field() self.assertIn('<li class="field-col col4', result)
class Appearance(BaseSetting, NavbarSection, Footer, ButtonAction, Colors): # Navbar settings header_logo = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=('Logo Image'), ) favicon_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=('Favicon'), ) # Body settings body_background_color_solid = ColorField( blank=True, null=True, help_text="Choose background color", verbose_name=('Background color'), ) # components tab panels components_panels = ButtonAction.button_action_panels + NavbarSection.navbar_panels # branding tab panels branding_panels = [ MultiFieldPanel( [ FieldRowPanel([ NativeColorPanel( 'color_primary', heading='Color 1', classname="col6"), NativeColorPanel('color_secondary', heading='Color 2', classname="col6"), ]), ], heading='Colors', ), MultiFieldPanel( [ FieldRowPanel([ ImageChooserPanel( 'header_logo', heading='Logo', classname="col12"), ImageChooserPanel( 'favicon_image', heading='Favicon', classname="col12"), ]), ], heading='Logo and Favicon', ), ] # Register Tabs edit_handler = TabbedInterface([ ObjectList(branding_panels, heading="Branding"), ObjectList(components_panels, heading="Components"), ])
class TestFieldRowPanel(TestCase): def setUp(self): self.request = RequestFactory().get('/') user = AnonymousUser() # technically, Anonymous users cannot access the admin self.request.user = user self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', date_from=date(2014, 7, 20), date_to=date(2014, 7, 21)) self.dates_panel = FieldRowPanel([ FieldPanel('date_from', classname='col4'), FieldPanel('date_to', classname='coltwo'), ]).bind_to_model(EventPage) def test_render_as_object(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-22'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to_instance( instance=self.event, form=form, request=self.request ) result = field_panel.render_as_object() # check that the populated form field is included self.assertIn('value="2014-07-22"', result) # there should be no errors on this field self.assertNotIn('<p class="error-message">', result) def test_render_as_field(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-22'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to_instance( instance=self.event, form=form, request=self.request ) result = field_panel.render_as_field() # check that label is output in the 'field' style self.assertIn('<label for="id_date_to">End date:</label>', result) self.assertNotIn('<legend>End date</legend>', result) # check that help text is included self.assertIn('Not required if event is on a single day', result) # check that the populated form field is included self.assertIn('value="2014-07-22"', result) # there should be no errors on this field self.assertNotIn('<p class="error-message">', result) def test_error_message_is_rendered(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-33'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to_instance( instance=self.event, form=form, request=self.request ) result = field_panel.render_as_field() self.assertIn('<p class="error-message">', result) self.assertIn('<span>Enter a valid date.</span>', result) def test_add_col_when_wrong_in_panel_def(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-33'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to_instance( instance=self.event, form=form, request=self.request ) result = field_panel.render_as_field() self.assertIn('<li class="field-col coltwo col6', result) def test_added_col_doesnt_change_siblings(self): form = self.EventPageForm( {'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-33'}, instance=self.event) form.is_valid() field_panel = self.dates_panel.bind_to_instance( instance=self.event, form=form, request=self.request ) result = field_panel.render_as_field() self.assertIn('<li class="field-col col4', result)
class Contact(AbstractBaseModel): INFO = 'info' BUSINESS = 'business' VISIT = 'visit' MESSAGE_CATEGORY_CHOICES = (('visit', 'Visit brewery'), ('info', 'Info on beers'), ('business', 'Business occasion')) sender_full_name = models.CharField(max_length=255, verbose_name='Sender full name') sender_email = models.EmailField(max_length=255, verbose_name='Sender email') sender_telephone = models.CharField(max_length=255, verbose_name='Sender phone number', null=True, blank=True) message_type = models.CharField(max_length=20, choices=MESSAGE_CATEGORY_CHOICES, default=INFO) subject = models.CharField(max_length=255, verbose_name='Message subject') body = models.TextField(verbose_name='Message body') panels = [ FieldRowPanel([ FieldPanel('sender_full_name', classname='col6'), FieldPanel('sender_email', classname='col6') ]), FieldRowPanel([ FieldPanel('sender_telephone', classname='col6'), FieldPanel('message_type', classname='col6') ]), FieldPanel('subject', classname='full'), FieldPanel('body', classname='full') ] + AbstractBaseModel.panels search_fields = [ index.SearchField('sender_full_name', partial_match=True), index.SearchField('sender_email', partial_match=True), index.SearchField('subject', partial_match=True), index.SearchField('body', partial_match=True) ] class Meta: app_label = 'contact' ordering = ['-created', 'sender_email'] verbose_name = 'Contact message' verbose_name_plural = 'Contact messages' def __str__(self): return 'From %s on %s' % (self.sender_email, self.created.strftime('%d/%M/%Y')) def get_formatted_message(self): from_email = settings.DEFAULT_FROM_EMAIL subject = settings.EMAIL_SUBJECT_PREFIX + self.subject text_body = self.sender_email + ' ha mandato un messaggio con il contenuto: \n'\ + self.body + '.\n Dati del messaggio:\n -Mittente: '\ + self.sender_full_name + '\n -Telefono: ' + self.sender_telephone\ + '\n -Tipo di messaggio: ' + self.message_type + '\n -Data invio: '\ + self.created.strftime('%d/%m/%Y') + '.' html_content = '<h4>' + self.sender_email + ' ha mandato un messaggio con il contenuto: </h4>'\ + '<p> + ' + self.body + '</p><br/><h4>Dati del messaggio:</h4><ul><li>Mittente: '\ + self.sender_full_name + '</li><li>Telefono: ' + self.sender_telephone + '</li>'\ + '<li>Tipo di messaggio: ' + self.message_type + '</li><li>Data invio: '\ + self.created.strftime('%d/%m/%Y') + '</li></ul>' message = EmailMultiAlternatives(subject, text_body, from_email, settings.RECIPIENTS) message.attach_alternative(html_content, 'text/html') return message def send_itself(self): message = self.get_formatted_message() message.send()