Source code for events.forms
import datetime
import decimal
import re
import uuid
import pytz
import six
from ajax_select import make_ajax_field
from ajax_select.fields import (AutoCompleteSelectField,
AutoCompleteSelectMultipleField)
from crispy_forms.bootstrap import (FormActions, InlineCheckboxes, InlineRadios, PrependedText, Tab, TabHolder)
from crispy_forms.helper import FormHelper
from crispy_forms.layout import (HTML, Div, Row, Column, Field, Hidden, Layout, Reset, Submit)
from django import forms
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db.models import Model, Q
from django.forms import ModelChoiceField, ModelMultipleChoiceField, ModelForm, SelectDateWidget, TextInput
from django.utils import timezone
# python multithreading bug workaround
from pagedown.widgets import PagedownWidget
from data.forms import DynamicFieldContainer, FieldAccessForm, FieldAccessLevel
from events.fields import GroupedModelChoiceField
from events.models import (BaseEvent, Billing, MultiBilling, BillingEmail, MultiBillingEmail,
Category, CCReport, Event, Event2019, EventAttachment, EventCCInstance, Extra,
ExtraInstance, Hours, Lighting, Location, Organization, OrganizationTransfer,
OrgBillingVerificationEvent, Workshop, WorkshopDate, Projection, Service, ServiceInstance,
Sound, PostEventSurvey, OfficeHour)
from events.widgets import ValueSelectField
from helpers.form_text import markdown_at_msgs
from helpers.util import curry_class
LIGHT_EXTRAS = Extra.objects.exclude(disappear=True).filter(category__name="Lighting")
LIGHT_EXTRAS_ID_NAME = LIGHT_EXTRAS.values_list('id', 'name')
LIGHT_EXTRAS_NAMES = LIGHT_EXTRAS.values('name')
SOUND_EXTRAS = Extra.objects.exclude(disappear=True).filter(category__name="Sound")
SOUND_EXTRAS_ID_NAME = SOUND_EXTRAS.values_list('id', 'name')
SOUND_EXTRAS_NAMES = SOUND_EXTRAS.values('name')
PROJ_EXTRAS = Extra.objects.exclude(disappear=True).filter(category__name="Projection")
PROJ_EXTRAS_ID_NAME = PROJ_EXTRAS.values_list('id', 'name')
PROJ_EXTRAS_NAMES = PROJ_EXTRAS.values('name')
JOBTYPES = (
(0, 'Lighting'),
(1, 'Sound'),
(2, 'Projection'),
(3, 'Other Services'),
)
LIGHT_CHOICES = (
(1, 'L1'),
(2, 'L2'),
(3, 'L3'),
(4, 'L4'),
)
SOUND_CHOICES = (
(1, 'S1'),
(2, 'S2'),
(3, 'S3'),
(4, 'S4'),
)
PROJ_CHOICES = (
(16, '16mm'),
(35, '35mm'),
('d', 'Digital'),
)
# gets a set of services from a given event
[docs]def get_qs_from_event(event):
if isinstance(event, Event):
if event.lighting:
lighting_id = event.lighting.id
else:
lighting_id = None
if event.sound:
sound_id = event.sound.id
else:
sound_id = None
if event.projection:
proj_id = event.projection.id
else:
proj_id = None
return Service.objects.filter(Q(id__in=[lighting_id]) | Q(id__in=[sound_id]) | Q(id__in=[proj_id]) | Q(
id__in=[i.id for i in event.otherservices.all()]))
elif isinstance(event, Event2019):
return Service.objects.filter(pk__in=event.serviceinstance_set.values_list('service', flat=True))
[docs]class CustomAutoCompleteSelectMultipleField(AutoCompleteSelectMultipleField):
[docs] def has_changed(self, initial_value, data_value):
initial_value = [v.pk if isinstance(v, Model) else v for v in (initial_value or [])]
return AutoCompleteSelectMultipleField.has_changed(self, initial_value, data_value)
[docs]class CustomEventModelMultipleChoiceField(forms.ModelMultipleChoiceField):
[docs] def label_from_instance(self, event):
return six.u('%s\u2014%s') % (event.event_name, ', '.join(map(lambda org: org.name, event.org.all())))
[docs]class SurveyCustomRadioSelect(forms.widgets.ChoiceWidget):
input_type = 'radio'
template_name = 'survey_custom_radio_select.html'
option_template_name = 'survey_custom_radio_select.html'
# LNAdmin Forms
[docs]class WorkorderSubmit(ModelForm):
[docs] class Meta:
model = Event
exclude = ('submitted_by', 'submitted_ip', 'approved', 'crew', 'crew_chief',
'report', 'closed', 'payment_amount', 'paid')
def __init__(self, *args, **kwargs):
super(WorkorderSubmit, self).__init__(*args, **kwargs)
self.fields['datetime_setup_start'].widget = SelectDateWidget()
# self.fields['datetime_start'].widget = datetime()
# self.fields['datetime_end'].widget = datetime()
[docs]class CrewAssign(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.layout = Layout(
Field('crew'),
FormActions(
Submit('save', 'Save Changes'),
)
)
super(CrewAssign, self).__init__(*args, **kwargs)
crew = make_ajax_field(Event, 'crew', 'Members', plugin_options={'minLength': 3})
[docs]class CrewChiefAssign(forms.ModelForm):
crew_chief = make_ajax_field(Event, 'crew_chief', 'Members', plugin_options={'minLength': 3})
[docs]class IOrgForm(FieldAccessForm):
""" Internal Organization Details Form """
def __init__(self, request_user, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
tabs = []
if request_user.has_perm('events.view_org_notes'):
tabs.append(
Tab(
'Notes',
'notes',
'delinquent'
)
)
read_only = 'rw'
instance = kwargs['instance']
if instance is not None and instance.locked is True:
read_only = 'r'
tabs.extend([
Tab(
'Contact',
Field('name', css_class=read_only),
'exec_email',
'address',
Field('phone', css_class="bfh-phone", data_format="(ddd) ddd dddd"),
),
Tab(
'Options',
'associated_orgs',
Field('personal', )
),
Tab(
'Money',
'workday_fund',
Field('worktag', placeholder='e.g. 1234-CC'),
),
Tab(
'People',
Field('user_in_charge', css_class=read_only),
'associated_users',
)
])
self.helper.layout = Layout(
TabHolder(*tabs),
FormActions(
Submit('save', 'Save Changes'),
)
)
super(IOrgForm, self).__init__(request_user, *args, **kwargs)
[docs] def clean_worktag(self):
pattern = re.compile('[0-9]+-[A-Z][A-Z]')
if pattern.match(self.cleaned_data['worktag']) is None and self.cleaned_data['worktag'] not in [None, '']:
raise ValidationError('What you entered is not a valid worktag. Here are some examples of what a worktag '
'looks like: 1234-CC, 123-AG')
return self.cleaned_data['worktag']
[docs] class Meta:
model = Organization
fields = ('name', 'exec_email', 'address', 'phone', 'associated_orgs', 'personal',
'workday_fund', 'worktag', 'user_in_charge', 'associated_users', 'notes', 'delinquent')
# associated_orgs = make_ajax_field(Organization,'associated_orgs','Orgs',plugin_options = {'minLength':2})
# associated_users = make_ajax_field(Organization,'associated_users','Users',plugin_options = {'minLength':3})
user_in_charge = AutoCompleteSelectField('Users')
associated_orgs = AutoCompleteSelectMultipleField('Orgs', required=False)
associated_users = AutoCompleteSelectMultipleField('Users', required=False)
worktag = forms.CharField(required=False, help_text='Ends in -AG, -CC, -GF, -GR, or -DE')
notes = forms.CharField(widget=PagedownWidget(), label="Internal Notes", required=False)
[docs] class FieldAccess:
def __init__(self):
pass
internal_notes_view = FieldAccessLevel(
lambda user, instance: not user.has_perm("events.view_org_notes", instance),
exclude=('notes', 'delinquent')
)
internal_notes_edit = FieldAccessLevel(
lambda user, instance: user.has_perm("events.view_org_notes", instance),
enable=('notes', 'delinquent')
)
billing_view = FieldAccessLevel(
lambda user, instance: not user.has_perm("events.show_org_billing", instance),
exclude=('workday_fund', 'worktag')
)
billing_edit = FieldAccessLevel(
lambda user, instance: user.has_perm("events.edit_org_billing", instance),
enable=('workday_fund', 'worktag')
)
members_view = FieldAccessLevel(
lambda user, instance: not user.has_perm("events.list_org_members", instance),
exclude=('user_in_charge', 'associated_users',)
)
members_edit = FieldAccessLevel(
lambda user, instance: user.has_perm("events.edit_org_members", instance),
enable=('associated_users',)
)
owner_edit = FieldAccessLevel(
lambda user, instance: user.has_perm("events.transfer_org_ownership", instance),
enable=('user_in_charge',)
)
everything_else_edit = FieldAccessLevel(
lambda user, instance: user.has_perm("events.edit_org", instance),
enable=('name', 'exec_email', 'address', 'phone', 'associated_orgs', 'personal')
)
[docs]class IOrgVerificationForm(forms.ModelForm):
""" Internal Client Billing Verification Form """
def __init__(self, org, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.org = org
self.helper.layout = Layout(
Field('date', css_class="datepick"),
Field('verified_by'),
Field('note', size="5"),
FormActions(
Submit('save', 'Verify'),
)
)
super(IOrgVerificationForm, self).__init__(*args, **kwargs)
[docs] def save(self, commit=True):
obj = super(IOrgVerificationForm, self).save(commit=False)
obj.org = self.org
if commit:
obj.save()
return obj
verified_by = AutoCompleteSelectField('Officers')
# Flow Forms
[docs]class EventApprovalForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EventApprovalForm, self).__init__(*args, **kwargs)
tabs = (
Tab(
"Standard Fields",
Field('description', label="Description (optional)", css_class="col-md-6"),
Field('internal_notes', label="Internal Notes", css_class="col-md-6"),
markdown_at_msgs,
Field('datetime_setup_complete', label="Setup Finish", css_class="dtp"),
Field('datetime_start', label="Event Start", css_class="dtp"),
Field('datetime_end', label="Event End", css_class="dtp"),
'org',
'billing_org',
'billed_in_bulk',
# Field('datetime_setup_start',label="Setup Start",css_class="dtp"),
active=True
),
)
if isinstance(self.instance, Event):
tabs += (
Tab(
"Services",
Field('lighting'),
Field('lighting_reqs', css_class="col-md-8"),
Field('sound'),
Field('sound_reqs', css_class="col-md-8"),
Field('projection'),
Field('proj_reqs', css_class="col-md-8"),
Field('otherservices'),
Field('otherservice_reqs', css_class="col-md-8")
),
)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.include_media = False
self.helper.form_class = "form-horizontal"
self.helper.layout = Layout(*tabs)
[docs] class Meta:
model = Event
fields = ['description', 'internal_notes', 'datetime_start', 'datetime_end', 'org', 'billing_org',
'billed_in_bulk', 'datetime_setup_complete', 'lighting', 'lighting_reqs',
'sound', 'sound_reqs', 'projection', 'proj_reqs', 'otherservices', 'otherservice_reqs']
widgets = {
'description': PagedownWidget(),
'internal_notes': PagedownWidget,
'lighting_reqs': PagedownWidget(),
'sound_reqs': PagedownWidget(),
'proj_reqs': PagedownWidget(),
'otherservice_reqs': PagedownWidget()
}
org = AutoCompleteSelectMultipleField('Orgs', required=False, label='Client(s)')
billing_org = AutoCompleteSelectField('Orgs', required=False, label='Client to bill')
datetime_start = forms.SplitDateTimeField(initial=timezone.now, label="Event Start")
datetime_end = forms.SplitDateTimeField(initial=timezone.now, label="Event End")
# datetime_setup_start = forms.SplitDateTimeField(initial=timezone.now)
datetime_setup_complete = forms.SplitDateTimeField(initial=timezone.now, label="Setup Completed")
[docs]class EventDenialForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.layout = Layout(
Field('cancelled_reason', label="Reason For Cancellation (optional)", css_class="col-md-6"),
Field('send_email', css_class=""),
HTML('Please note that the reason will be included in the cancellation email to the event contact. <p />'),
FormActions(
Submit('save', 'Deny Event'),
),
)
super(EventDenialForm, self).__init__(*args, **kwargs)
send_email = forms.BooleanField(required=False, label="Send Cancellation Email to Event Contact")
[docs] class Meta:
model = BaseEvent
fields = ['cancelled_reason', 'send_email']
widgets = {
'cancelled_reason': PagedownWidget()
}
[docs]class EventMeetingForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.layout = Layout(
Field('datetime_setup_start', label="Setup Start", css_class="dtp"),
Field('datetime_setup_complete', label="Setup Finish", css_class="dtp"),
Field('crew_chief', label="Crew Chief"),
Field('crew', label="Crew"),
FormActions(
Submit('save', 'Update Event and Return'),
)
)
super(EventMeetingForm, self).__init__(*args, **kwargs)
[docs] class Meta:
model = Event
fields = ['datetime_setup_start', 'datetime_setup_complete', 'crew_chief', 'crew']
datetime_setup_start = forms.SplitDateTimeField(initial=timezone.now)
datetime_setup_complete = forms.SplitDateTimeField(initial=timezone.now)
crew_chief = AutoCompleteSelectMultipleField('Users', required=False)
crew = AutoCompleteSelectMultipleField('3', required=False)
[docs]class InternalEventForm(FieldAccessForm):
def __init__(self, request_user, *args, **kwargs):
super(InternalEventForm, self).__init__(request_user, *args, **kwargs)
tabs = (
Tab(
'Name And Location',
'event_name',
'location',
'lnl_contact',
Field('description'),
DynamicFieldContainer('internal_notes'),
'billed_in_bulk',
'sensitive',
'test_event',
active=True
),
Tab(
'Contact',
'contact',
'org',
DynamicFieldContainer('billing_org'),
),
Tab(
'Scheduling',
Div(
Div(Field('datetime_setup_complete', css_class='dtp', title="Setup Completed By"),
css_class="padleft"),
),
Div(
HTML(
'<div class="pull-left pushdown"><br />'
'<a class="btn btn-primary" href="#" id="samedate1" title="Cascade Dates">'
'<i class="glyphicon glyphicon-resize-small icon-white"></i> '
'<i class="glyphicon glyphicon-calendar icon-white"></i></a></div>'),
Div(Field('datetime_start', css_class='dtp'), css_class="padleft"),
),
Div(
HTML(
'<div class="pull-left pushdown"><br />'
'<a class="btn btn-primary" href="#" id="samedate2" title="Cascade Dates">'
'<i class="glyphicon glyphicon-resize-small icon-white"></i> '
'<i class="glyphicon glyphicon-calendar icon-white"></i></a></div>'),
Div(Field('datetime_end', css_class='dtp'), css_class="padleft"),
),
),
Tab(
'Lighting',
'lighting',
'lighting_reqs'
),
Tab(
'Sound',
'sound',
'sound_reqs'
),
Tab(
'Projection',
'projection',
'proj_reqs'
),
Tab(
'Other Services',
'otherservices',
'otherservice_reqs'
),
)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.include_media = False
self.helper.layout = Layout(*tabs)
[docs] class FieldAccess:
def __init__(self):
pass
internal_notes_write = FieldAccessLevel(
lambda user, instance: user.has_perm("events.event_view_sensitive", instance),
enable=('internal_notes',)
)
hide_internal_notes = FieldAccessLevel(
lambda user, instance: not user.has_perm("events.event_view_sensitive", instance),
exclude=('internal_notes',)
)
event_times = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_times', instance),
enable=('datetime_start', 'datetime_setup_complete', 'datetime_end')
)
edit_descriptions = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_text', instance),
enable=('event_name', 'location', 'description',
'lighting_reqs', 'sound_reqs', 'proj_reqs', 'otherservice_reqs')
)
change_owner = FieldAccessLevel(
lambda user, instance: user.has_perm('events.adjust_event_owner', instance),
enable=('contact', 'org')
)
change_type = FieldAccessLevel(
lambda user, instance: user.has_perm('events.adjust_event_charges', instance),
enable=('lighting', 'sound', 'projection', 'otherservices', 'billed_in_bulk')
)
billing_edit = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_fund', instance),
enable=('billing_org', 'billed_in_bulk')
)
billing_view = FieldAccessLevel(
lambda user, instance: not user.has_perm('events.view_event_billing', instance),
exclude=('billing_org', 'billed_in_bulk')
)
change_flags = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_flags', instance),
enable=('sensitive', 'test_event')
)
change_lnl_contact = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_lnl_contact', instance),
enable=('lnl_contact')
)
[docs] class Meta:
model = Event
fields = ('event_name', 'location', 'lnl_contact', 'description', 'internal_notes', 'billing_org', 'billed_in_bulk', 'contact',
'org', 'datetime_setup_complete', 'datetime_start', 'datetime_end', 'lighting', 'lighting_reqs',
'sound', 'sound_reqs', 'projection', 'proj_reqs', 'otherservices', 'otherservice_reqs', 'sensitive',
'test_event')
widgets = {
'description': PagedownWidget(),
'internal_notes': PagedownWidget,
'lighting_reqs': PagedownWidget(),
'sound_reqs': PagedownWidget(),
'proj_reqs': PagedownWidget(),
'otherservice_reqs': PagedownWidget()
}
location = GroupedModelChoiceField(
queryset=Location.objects.filter(show_in_wo_form=True),
group_by_field="building",
group_label=lambda group: group.name,
)
contact = AutoCompleteSelectField('Users', required=False)
lnl_contact = AutoCompleteSelectField('Members', required=False)
org = CustomAutoCompleteSelectMultipleField('Orgs', required=False, label="Client(s)")
billing_org = AutoCompleteSelectField('Orgs', required=False, label="Client to bill")
datetime_setup_complete = forms.SplitDateTimeField(initial=timezone.now, label="Setup Completed")
datetime_start = forms.SplitDateTimeField(initial=timezone.now, label="Event Start")
datetime_end = forms.SplitDateTimeField(initial=timezone.now, label="Event End")
otherservices = ModelMultipleChoiceField(queryset=Service.objects.filter(enabled_event2012=True), required=False)
[docs]class InternalEventForm2019(FieldAccessForm):
def __init__(self, request_user, *args, **kwargs):
super(InternalEventForm2019, self).__init__(request_user, *args, **kwargs)
tabs = (
Tab(
'Name And Location',
'event_name',
'location',
'lnl_contact',
'reference_code',
Field('description'),
DynamicFieldContainer('internal_notes'),
HTML('<div style="width: 50%">'),
'max_crew',
'cancelled_reason',
HTML('</div>'),
'billed_in_bulk',
'sensitive',
'test_event',
'entered_into_workday',
'send_survey',
active=True
),
Tab(
'Contact',
'contact',
'org',
DynamicFieldContainer('billing_org'),
),
Tab(
'Scheduling',
Div(
Div(Field('datetime_setup_complete', css_class='dtp', title="Setup Completed By"),
css_class="padleft"),
),
Div(
HTML(
'<div class="pull-left pushdown"><br />'
'<a class="btn btn-primary" href="#" id="samedate1" title="Cascade Dates">'
'<i class="glyphicon glyphicon-resize-small icon-white"></i> '
'<i class="glyphicon glyphicon-calendar icon-white"></i></a></div>'),
Div(Field('datetime_start', css_class='dtp'), css_class="padleft"),
),
Div(
HTML(
'<div class="pull-left pushdown"><br />'
'<a class="btn btn-primary" href="#" id="samedate2" title="Cascade Dates">'
'<i class="glyphicon glyphicon-resize-small icon-white"></i> '
'<i class="glyphicon glyphicon-calendar icon-white"></i></a></div>'),
Div(Field('datetime_end', css_class='dtp'), css_class="padleft"),
),
),
)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.include_media = False
self.helper.layout = Layout(*tabs)
[docs] class FieldAccess:
def __init__(self):
pass
internal_notes_write = FieldAccessLevel(
lambda user, instance: user.has_perm("events.event_view_sensitive", instance),
enable=('internal_notes',)
)
hide_internal_notes = FieldAccessLevel(
lambda user, instance: not user.has_perm("events.event_view_sensitive", instance),
exclude=('internal_notes',)
)
event_times = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_times', instance),
enable=('datetime_start', 'datetime_setup_complete', 'datetime_end',
'reference_code')
)
edit_descriptions = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_text', instance),
enable=('event_name', 'location', 'description',
'lighting_reqs', 'sound_reqs', 'proj_reqs', 'otherservice_reqs', 'max_crew')
)
change_owner = FieldAccessLevel(
lambda user, instance: user.has_perm('events.adjust_event_owner', instance),
enable=('contact', 'org', 'reference_code')
)
change_type = FieldAccessLevel(
lambda user, instance: user.has_perm('events.adjust_event_charges', instance),
enable=('lighting', 'sound', 'projection', 'otherservices', 'billed_in_bulk')
)
billing_edit = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_fund', instance),
enable=('billing_org', 'billed_in_bulk')
)
billing_view = FieldAccessLevel(
lambda user, instance: not user.has_perm('events.view_event_billing', instance),
exclude=('billing_org', 'billed_in_bulk', 'entered_into_workday')
)
change_flags = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_flags', instance),
enable=('sensitive', 'test_event')
)
change_lnl_contact = FieldAccessLevel(
lambda user, instance: user.has_perm('events.edit_event_lnl_contact', instance),
enable=('lnl_contact')
)
change_entered_into_workday = FieldAccessLevel(
lambda user, instance: user.has_perm('events.bill_event', instance),
enable=('entered_into_workday',)
)
survey_edit = FieldAccessLevel(
lambda user, instance: user.has_perm('events.view_posteventsurveyresults') and \
(instance is None or not instance.survey_sent), enable=('send_survey',)
)
survey_view = FieldAccessLevel(
lambda user, instance: not user.has_perm('events.view_posteventsurveyresults'),
exclude=('send_survey',)
)
cancelled_reason_edit = FieldAccessLevel(
lambda user, instance: user.has_perm('events.cancel_event') and \
(instance is not None and instance.cancelled), enable=('cancelled_reason',)
)
[docs] class Meta:
model = Event2019
fields = ('event_name', 'location', 'lnl_contact', 'description', 'internal_notes', 'billing_org',
'billed_in_bulk', 'contact', 'org', 'datetime_setup_complete', 'datetime_start',
'datetime_end', 'sensitive', 'test_event',
'entered_into_workday', 'send_survey', 'max_crew','cancelled_reason',
'reference_code')
widgets = {
'description': PagedownWidget(),
'internal_notes': PagedownWidget(),
'cancelled_reason': TextInput(),
}
location = GroupedModelChoiceField(
queryset=Location.objects.filter(show_in_wo_form=True),
group_by_field="building",
group_label=lambda group: group.name,
)
contact = AutoCompleteSelectField('Users', required=False)
lnl_contact = AutoCompleteSelectField('Members', label="LNL Contact (PM)", required=False)
org = CustomAutoCompleteSelectMultipleField('Orgs', required=False, label="Client(s)")
billing_org = AutoCompleteSelectField('Orgs', required=False, label="Client to bill")
datetime_setup_complete = forms.SplitDateTimeField(initial=timezone.now, label="Setup Completed")
datetime_start = forms.SplitDateTimeField(initial=timezone.now, label="Event Start")
datetime_end = forms.SplitDateTimeField(initial=timezone.now, label="Event End")
max_crew = forms.IntegerField(label="Maximum Crew", help_text="Include this to enforce an occupancy limit",
required=False)
cancelled_reason = forms.CharField(label="Reason for Cancellation", required=False),
# Regex will match valid 25Live reservation codes in the format
# `2022-ABNXQQ`
reference_code =forms.CharField(validators=[RegexValidator(regex=r"[0-9]{4}-[A-Z]{6}")],
required = False)
[docs]class EventReviewForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
event = kwargs.pop('event')
self.helper = FormHelper()
self.helper.layout = Layout(
'org',
'billing_org',
Field('internal_notes', css_class="col-md-6", size="15"),
FormActions(
HTML('<h4> Does this look good to you?</h4>'),
Submit('save', 'Yes!', css_class='btn btn-lg btn-success'),
HTML(
'<a class="btn btn-lg btn-danger" href="{%% url "events:detail" %s %%}"> Cancel </a>'
% event.id),
),
)
super(EventReviewForm, self).__init__(*args, **kwargs)
[docs] class Meta:
model = Event
fields = ('org', 'billing_org', 'internal_notes')
widgets = {
'internal_notes': PagedownWidget()
}
org = AutoCompleteSelectMultipleField('Orgs', required=True, label="Client(s)")
billing_org = AutoCompleteSelectField('Orgs', required=False, label="Client to bill")
[docs]class InternalReportForm(FieldAccessForm):
""" Crew Chief Report Form """
def __init__(self, event, *args, **kwargs):
self.event = event
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
DynamicFieldContainer('crew_chief'),
HTML('<h4>What you might put in the report:</h4>'
'<ul><li>How was the event set up?</li>'
'<li>Roughly what equipment was used?</li>'
'<li>Were there any last minute changes?</li>'
'<li>Did you come across any issues?</li>'
'<li>Would you classify this event as the level it was booked under?</li>'
'<li>What information would be useful for somebody next year?</li></ul>'),
Field('report', css_class="col-md-10"),
markdown_at_msgs,
FormActions(
Submit('save', 'Save Changes'),
# Reset('reset','Reset Form'),
)
)
super(InternalReportForm, self).__init__(*args, **kwargs)
if 'crew_chief' in self.fields and not self.fields['crew_chief'].initial:
self.fields['crew_chief'].initial = self.user.pk
[docs] def save(self, commit=True):
obj = super(InternalReportForm, self).save(commit=False)
if 'crew_chief' not in self.cleaned_data:
self.instance.crew_chief = self.user # user field from FAF
obj.event = self.event
if commit:
obj.save()
self.save_m2m()
return obj
[docs] class Meta:
model = CCReport
fields = ('crew_chief', 'report')
widgets = {
'report': PagedownWidget()
}
crew_chief = AutoCompleteSelectField('Members', required=False)
[docs] class FieldAccess:
def __init__(self):
pass
avg_user = FieldAccessLevel(
lambda user, instance: not user.has_perm('events.add_event_report'),
exclude=('crew_chief',)
)
admin = FieldAccessLevel(
lambda user, instance: user.has_perm('events.add_event_report'),
enable=('crew_chief',)
)
all = FieldAccessLevel(lambda user, instance: True, enable=('report',))
# External Organization forms
[docs]class ExternalOrgUpdateForm(forms.ModelForm):
""" Organization Details Form for Client """
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = 'post'
self.helper.form_action = ''
self.helper.layout = Layout(
'exec_email',
'address',
Field('phone', css_class="bfh-phone", data_format="(ddd) ddd dddd"),
'associated_users',
'workday_fund',
Field('worktag', placeholder='e.g. 1234-CC'),
FormActions(
Submit('save', 'Save Changes'),
)
)
super(ExternalOrgUpdateForm, self).__init__(*args, **kwargs)
associated_users = AutoCompleteSelectMultipleField('Users', required=True)
worktag = forms.CharField(required=False, help_text='Ends in -AG, -CC, -GF, -GR, or -DE')
[docs] def clean_worktag(self):
pattern = re.compile('[0-9]+-[A-Z][A-Z]')
if pattern.match(self.cleaned_data['worktag']) is None and self.cleaned_data['worktag'] not in [None, '']:
raise ValidationError('What you entered is not a valid worktag. Here are some examples of what a worktag '
'looks like: 1234-CC, 123-AG')
return self.cleaned_data['worktag']
[docs] class Meta:
model = Organization
fields = ('exec_email', 'address', 'phone', 'associated_users', 'workday_fund', 'worktag')
[docs]class OrgXFerForm(forms.ModelForm):
""" Organization Transfer Form """
def __init__(self, org, user, *args, **kwargs):
self.user = user
self.org = org
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = 'post'
self.helper.form_action = ''
self.helper.layout = Layout(
'new_user_in_charge',
HTML(
'<p class="text-muted">'
'This form will transfer ownership of this Organization to another user associated '
'with the Organization. A confirmation E-Mail will be sent with a link to confirm the '
'transfer.</p>'),
FormActions(
Submit('save', 'Submit Transfer'),
)
)
super(OrgXFerForm, self).__init__(*args, **kwargs)
self.fields['new_user_in_charge'].queryset = org.associated_users.exclude(id=org.user_in_charge_id)
[docs] def save(self, commit=True):
obj = super(OrgXFerForm, self).save(commit=False)
obj.initiator = self.user
obj.old_user_in_charge = self.org.user_in_charge
obj.org = self.org
if not obj.uuid:
obj.uuid = uuid.uuid4()
if commit:
obj.save()
return obj
# new_user_in_charge = AutoCompleteSelectField('Users', required=True,
# plugin_options={'position':"{ my : \"right top\", at: \"right bottom\",
# of: \"#id_person_name_text\"},'minlength':4"})
[docs]class SelfServiceOrgRequestForm(forms.Form):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = 'post'
self.helper.form_action = ''
self.helper.help_text_inline = True
self.helper.layout = Layout(
Field('client_name', help_text_inline=True),
'email',
'address',
Field('phone', css_class="bfh-phone", data_format="(ddd) ddd dddd"),
FormActions(
Submit('save', 'Submit Request'),
)
)
super(SelfServiceOrgRequestForm, self).__init__(*args, **kwargs)
client_name = forms.CharField(max_length=128, label="Client Name", help_text="EX: Lens & Lights")
email = forms.EmailField(help_text="EX: lnl@wpi.edu (This should be your exec board alias)")
address = forms.CharField(widget=forms.Textarea, help_text="EX: Campus Center 339")
phone = forms.CharField(max_length=15, help_text="EX: (508) - 867 - 5309")
[docs]class WorkdayForm(forms.ModelForm):
""" Workday Bill Pay Form """
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.layout = Layout(
'workday_fund',
Field('worktag', placeholder='e.g. 1234-CC'),
'workday_form_comments',
FormActions(
Submit('submit', 'Pay'),
)
)
super(WorkdayForm, self).__init__(*args, **kwargs)
self.fields['workday_fund'].label = 'Funding source'
self.fields['workday_fund'].required = True
self.fields['workday_form_comments'].label = 'Comments'
[docs] def clean_worktag(self):
pattern = re.compile('[0-9]+-[A-Z][A-Z]')
if pattern.match(self.cleaned_data['worktag']) is None:
raise ValidationError('What you entered is not a valid worktag. Here are some examples of what a worktag '
'looks like: 1234-CC, 123-AG')
return self.cleaned_data['worktag']
worktag = forms.CharField(required=True, help_text='Ends in -AG, -CC, -GF, -GR, or -DE')
# Internal Billing forms
[docs]class BillingForm(forms.ModelForm):
def __init__(self, event, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
self.event = event
self.helper.layout = Layout(
PrependedText('date_billed', '<i class="glyphicon glyphicon-calendar"></i>', css_class="datepick"),
PrependedText('amount', '<strong>$</strong>'),
FormActions(
Submit('save-and-return', 'Save and Return'),
Submit('save-and-make-email', 'Save and Make Email'),
Reset('reset', 'Reset Form'),
)
)
super(BillingForm, self).__init__(*args, **kwargs)
self.fields['amount'].initial = "%.2f" % event.cost_total
self.fields['date_billed'].initial = datetime.date.today()
[docs] def save(self, commit=True):
obj = super(BillingForm, self).save(commit=False)
obj.event = self.event
if commit:
obj.save()
return obj
[docs]class BillingUpdateForm(forms.ModelForm):
def __init__(self, event, *args, **kwargs):
self.event = event
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
PrependedText('date_paid', '<i class="glyphicon glyphicon-calendar"></i>', css_class="datepick"),
PrependedText('amount', '<strong>$</strong>'),
FormActions(
Submit('save', 'Save Changes'),
Reset('reset', 'Reset Form'),
)
)
super(BillingUpdateForm, self).__init__(*args, **kwargs)
self.fields['amount'].initial = str(event.cost_total)
self.fields['date_paid'].initial = datetime.date.today()
[docs] def save(self, commit=True):
obj = super(BillingUpdateForm, self).save(commit=False)
obj.event = self.event
if commit:
obj.save()
return obj
[docs]class MultiBillingForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
show_nonbulk_events = kwargs.pop('show_nonbulk_events')
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
layout = []
if show_nonbulk_events:
layout += [
HTML(
'<a href="?show_nonbulk_events=false">Switch back to showing only events marked for '
'semester billing</a>')
]
else:
layout += [
HTML('<a href="?show_nonbulk_events=true">Show events not marked for semester billing</a>')
]
layout += [
'events',
PrependedText('date_billed', '<i class="glyphicon glyphicon-calendar"></i>', css_class="datepick"),
PrependedText('amount', '<strong>$</strong>'),
FormActions(
Submit('save-and-return', 'Save and Return'),
Submit('save-and-make-email', 'Save and Make Email'),
Reset('reset', 'Reset Form'),
)
]
self.helper.layout = Layout(*layout)
super(MultiBillingForm, self).__init__(*args, **kwargs)
if show_nonbulk_events is True:
self.fields['events'].queryset = BaseEvent.objects.filter(closed=False, reviewed=True,
billings__isnull=True) \
.exclude(multibillings__isnull=False, multibillings__date_paid__isnull=False)
self.fields['events'].help_text = 'Only unbilled, reviewed events are listed above.'
self.fields['date_billed'].initial = datetime.date.today()
[docs] def clean(self):
cleaned_data = super(MultiBillingForm, self).clean()
events = cleaned_data.get('events', [])
if events:
org = events[0].org_to_be_billed
for event in events[1:]:
if event.org_to_be_billed != org:
raise ValidationError('All events you select must have the same \'Client to bill\'.')
return cleaned_data
[docs] def save(self, commit=True):
instance = super(MultiBillingForm, self).save(commit=False)
instance.org = self.cleaned_data['events'].first().org_to_be_billed
if commit:
instance.save()
self.save_m2m()
return instance
events = CustomEventModelMultipleChoiceField(
queryset=BaseEvent.objects.filter(closed=False, reviewed=True, billings__isnull=True, billed_in_bulk=True) \
.exclude(multibillings__isnull=False, multibillings__date_paid__isnull=False),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'checkbox'}),
help_text="Only unbilled, reviewed events that are marked for semester billing are listed above."
)
[docs]class MultiBillingUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MultiBillingUpdateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
events_str = ', '.join(map(lambda event: event.event_name, self.instance.events.all()))
self.helper.layout = Layout(
HTML('<p>Events: ' + events_str + '</p>'),
PrependedText('date_paid', '<i class="glyphicon glyphicon-calendar"></i>', css_class="datepick"),
PrependedText('amount', '<strong>$</strong>'),
FormActions(
Submit('save', 'Save Changes'),
Reset('reset', 'Reset Form'),
)
)
self.fields['date_paid'].initial = datetime.date.today()
[docs]class BillingEmailForm(forms.ModelForm):
def __init__(self, billing, *args, **kwargs):
super(BillingEmailForm, self).__init__(*args, **kwargs)
self.billing = billing
orgs = billing.event.org.all()
self.fields["email_to_orgs"].queryset = orgs
self.fields["email_to_orgs"].initial = orgs
if billing.event.contact:
self.fields["email_to_users"].initial = [billing.event.contact.pk]
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-10'
self.helper.layout = Layout(
HTML('<p class="text-muted">The bill PDF for this event will be attached to the email. The message you '
'type below is not the entire contents of the email; a large "PAY NOW" button and identifying '
'information about the event being billed will be added to the email below your message.</p>'),
'subject',
'message',
'email_to_users',
'email_to_orgs',
FormActions(
Submit('save', 'Send Email'),
)
)
[docs] def clean(self):
cleaned_data = super(BillingEmailForm, self).clean()
if not cleaned_data.get('email_to_users', None) and not cleaned_data.get('email_to_orgs', None):
raise ValidationError('No recipients')
for user in map(lambda id: get_user_model().objects.get(id=id), cleaned_data.get('email_to_users', [])):
if not user.email:
raise ValidationError('User %s has no email address on file' % user.get_full_name())
for org in cleaned_data.get('email_to_orgs', []):
if not org.exec_email:
raise ValidationError('Organization %s has no email address on file' % org.name)
return cleaned_data
[docs] def save(self, commit=True):
instance = super(BillingEmailForm, self).save(commit=False)
instance.billing = self.billing
if commit:
instance.save()
self.save_m2m()
return instance
[docs] class Meta:
model = BillingEmail
fields = ('subject', 'message', 'email_to_users', 'email_to_orgs')
email_to_users = AutoCompleteSelectMultipleField('Users', required=False, label="User Recipients")
email_to_orgs = CustomOrganizationEmailModelMultipleChoiceField(
queryset=Organization.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'checkbox'}),
required=False, label="Client Recipients",
help_text="The email address listed is the client's exec email alias on file."
)
[docs]class MultiBillingEmailForm(forms.ModelForm):
def __init__(self, multibilling, *args, **kwargs):
super(MultiBillingEmailForm, self).__init__(*args, **kwargs)
self.multibilling = multibilling
orgsets = map(lambda event: event.org.all(), multibilling.events.all())
orgs = next(iter(orgsets))
for orgset in orgsets:
orgs |= orgset
orgs = orgs.distinct()
contacts = map(lambda event: event.contact.pk, multibilling.events.filter(contact__isnull=False))
self.fields["email_to_orgs"].queryset = orgs
self.fields["email_to_orgs"].initial = multibilling.org
self.fields["email_to_users"].initial = contacts
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-10'
self.helper.layout = Layout(
HTML('<p class="text-muted">The bill PDF will be attached to the email.</p>'),
'subject',
'message',
'email_to_users',
'email_to_orgs',
FormActions(
Submit('save', 'Send Email'),
)
)
[docs] def clean(self):
cleaned_data = super(MultiBillingEmailForm, self).clean()
if not cleaned_data.get('email_to_users', None) and not cleaned_data.get('email_to_orgs', None):
raise ValidationError('No recipients')
for user in map(lambda id: get_user_model().objects.get(id=id), cleaned_data.get('email_to_users', [])):
if not user.email:
raise ValidationError('User %s has no email address on file' % user.get_full_name())
for org in cleaned_data.get('email_to_orgs', []):
if not org.exec_email:
raise ValidationError('Organization %s has no email address on file' % org.name)
return cleaned_data
[docs] def save(self, commit=True):
instance = super(MultiBillingEmailForm, self).save(commit=False)
instance.multibilling = self.multibilling
if commit:
instance.save()
self.save_m2m()
return instance
[docs] class Meta:
model = MultiBillingEmail
fields = ('subject', 'message', 'email_to_users', 'email_to_orgs')
email_to_users = AutoCompleteSelectMultipleField('Users', required=False, label="User Recipients")
email_to_orgs = CustomOrganizationEmailModelMultipleChoiceField(
queryset=Organization.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'checkbox'}),
required=False, label="Client Recipients",
help_text="The email address listed is the client's exec email alias on file."
)
# CC Facing Forms
[docs]class MKHoursForm(forms.ModelForm):
""" Event Hours Form """
def __init__(self, event, *args, **kwargs):
self.event = event
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
Field('user'),
Field('hours'),
Field('service'),
FormActions(
Submit('save', 'Save Changes'),
# Reset('reset','Reset Form'),
)
)
super(MKHoursForm, self).__init__(*args, **kwargs)
self.fields['service'].queryset = get_qs_from_event(event)
if isinstance(event, Event2019):
self.fields['category'].queryset = Category.objects.filter(
pk__in=event.serviceinstance_set.values_list('service__category', flat=True))
[docs] def clean(self):
super(MKHoursForm, self).clean()
category = self.cleaned_data.get('category')
service = self.cleaned_data.get('service')
user = self.cleaned_data.get('user')
if user is None:
# this problem will raise an error elsewhere
return
if self.event.hours.filter(user=user, category=category, service=service).exists() and not self.instance.pk:
raise ValidationError("User already has hours for this category/service. Edit those instead")
[docs] def save(self, commit=True):
obj = super(MKHoursForm, self).save(commit=False)
if obj.category is None and obj.service is not None:
obj.category = obj.service.category
obj.event = self.event
if commit:
obj.save()
return obj
user = AutoCompleteSelectField('Users', required=True)
hours = forms.DecimalField(min_value=decimal.Decimal("0.00"))
category = ModelChoiceField(queryset=Category.objects.all(), required=False)
service = ModelChoiceField(queryset=Service.objects.all(), required=False) # queryset gets changed in constructor
[docs]class EditHoursForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
Field('hours'),
FormActions(
Submit('save', 'Save Changes'),
)
)
super(EditHoursForm, self).__init__(*args, **kwargs)
[docs]class CCIForm(forms.ModelForm):
""" Crew Chief Instance form """
def __init__(self, event, *args, **kwargs):
self.event = event
self.helper = FormHelper()
self.helper.form_class = "form-inline"
self.helper.template = 'bootstrap/table_inline_formset.html'
self.helper.form_tag = False
self.helper.layout = Layout(
Field('crew_chief', placeholder="Crew Chief", title=""),
Field('service' if isinstance(event, Event) else 'category'),
Field('setup_location'),
Field('setup_start', css_class="dtp"),
HTML('<hr>'),
)
super(CCIForm, self).__init__(*args, **kwargs)
# x = self.instance.event.lighting
self.fields['service'].queryset = get_qs_from_event(event)
if isinstance(event, Event2019):
self.fields['category'].queryset = Category.objects.filter(
pk__in=event.serviceinstance_set.values_list('service__category', flat=True))
self.fields['setup_start'].initial = self.fields['setup_start'].prepare_value(
self.event.datetime_setup_complete.replace(second=0, microsecond=0)
)
[docs] def clean(self):
cleaned_data = super(CCIForm, self).clean()
if cleaned_data.get('category') is None and cleaned_data.get('service') is None:
self.add_error('category', 'category/service is a required field')
self.add_error('service', 'category/service is a required field')
return cleaned_data
[docs] def save(self, commit=True):
obj = super(CCIForm, self).save(commit=False)
try:
obj.category
except Category.DoesNotExist:
try:
obj.category = obj.service.category
except Service.DoesNotExist:
pass
obj.event = self.event
if commit:
obj.save()
return obj
[docs] class Meta:
model = EventCCInstance
fields = ('category', 'crew_chief', 'service', 'setup_location', 'setup_start')
crew_chief = AutoCompleteSelectField('Members', required=True)
setup_start = forms.SplitDateTimeField(initial=timezone.now)
setup_location = GroupedModelChoiceField(
queryset=Location.objects.filter(setup_only=True).select_related('building'),
group_by_field="building",
group_label=lambda group: group.name,
)
category = ModelChoiceField(queryset=Category.objects.all(), required=False)
service = ModelChoiceField(queryset=Service.objects.all(), required=False) # queryset gets changed in constructor
# Forms for Inline Formsets
[docs]class AttachmentForm(forms.ModelForm):
def __init__(self, event, externally_uploaded=False, *args, **kwargs):
self.event = event
self.helper = FormHelper()
self.helper.form_class = "form-inline"
self.helper.template = 'bootstrap/table_inline_formset.html'
self.helper.form_tag = False
self.helper.layout = Layout(
Field('for_service'),
Field('attachment'),
Field('note', size="2"),
Hidden('externally_uploaded', externally_uploaded),
HTML('<hr>'),
)
super(AttachmentForm, self).__init__(*args, **kwargs)
# x = self.instance.event.lighting
self.fields['for_service'].queryset = get_qs_from_event(event)
[docs] def save(self, commit=True):
obj = super(AttachmentForm, self).save(commit=False)
obj.event = self.event
if commit:
obj.save()
self.save_m2m()
return obj
[docs]class ExtraForm(forms.ModelForm):
extra = GroupedModelChoiceField(
queryset=Extra.objects.filter(disappear=False),
group_by_field="category",
group_label=lambda group: group.name,
)
[docs]class ServiceInstanceForm(forms.ModelForm):
def __init__(self, event, *args, **kwargs):
self.event = event
super(ServiceInstanceForm, self).__init__(*args, **kwargs)
[docs] def save(self, commit=True):
obj = super(ServiceInstanceForm, self).save(commit=False)
obj.event = self.event
if commit:
obj.save()
return obj
[docs] class Meta:
model = ServiceInstance
fields = ('service', 'detail')
widgets = {
'detail': PagedownWidget(show_preview=False),
}
service = ModelChoiceField(queryset=Service.objects.filter(enabled_event2019=True))
# CrewChiefFS = inlineformset_factory(Event,EventCCInstance,extra=3,form=CCIForm, exclude=[])
# usage
# CrewChiefFS = inlineformset_factory(Event,EventCCInstance,extra=3, exclude=[])
# CrewChiefFS.form = staticmethod(curry(CCIForm, event=request.event))
# Workorder Forms
# Workorder Repeat Form
[docs]class WorkorderRepeatForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.layout = Layout(
Field('location'),
Field('event_name'),
TabHolder(
Tab(
"Main Information",
Field('description', label="Description (optional)", css_class="col-md-6"),
Field('datetime_setup_complete', label="Setup Finish", css_class="dtp"),
Field('datetime_start', label="Event Start", css_class="dtp"),
Field('datetime_end', label="Event End", css_class="dtp"),
),
Tab(
"Lighting",
InlineRadios('lighting', title="Lighting", empty_label=None),
Field('lighting_reqs', css_class="col-md-8"),
),
Tab(
"Sound",
InlineRadios('sound', title="Sound"),
Field('sound_reqs', css_class="col-md-8"),
),
Tab(
"Projection",
InlineRadios('projection', title="Projection"),
Field('proj_reqs', css_class="col-md-8"),
),
Tab(
"Additional Information",
InlineCheckboxes('otherservices'),
Field('otherservice_reqs', css_class="col-md-8")
),
),
FormActions(
Submit('save', 'Repeat Event'),
),
)
super(WorkorderRepeatForm, self).__init__(*args, **kwargs)
datetime_setup_complete = forms.SplitDateTimeField(label="Setup Completed By", required=True)
datetime_start = forms.SplitDateTimeField(label="Event Starts")
datetime_end = forms.SplitDateTimeField(label="Event Ends")
location = GroupedModelChoiceField(
queryset=Location.objects.filter(show_in_wo_form=True),
group_by_field="building",
group_label=lambda group: group.name,
)
lighting = forms.ModelChoiceField(
empty_label=None,
queryset=Lighting.objects.all(),
widget=forms.RadioSelect(attrs={'class': 'radio itt'}),
required=False
)
sound = forms.ModelChoiceField(
empty_label=None,
queryset=Sound.objects.all(),
widget=forms.RadioSelect(attrs={'class': 'radio itt'}),
required=False
)
projection = forms.ModelChoiceField(
empty_label=None,
queryset=Projection.objects.all(),
widget=forms.RadioSelect(attrs={'class': 'radio itt'}),
required=False
)
otherservices = forms.ModelMultipleChoiceField(
queryset=Service.objects.filter(category__name__in=["Misc", "Power"]),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'checkbox'}),
required=False
)
[docs] class Meta:
model = Event
fields = ['location', 'event_name', 'description', 'datetime_start', 'datetime_end', 'datetime_setup_complete',
'lighting', 'lighting_reqs', 'sound', 'sound_reqs', 'projection', 'proj_reqs', 'otherservices',
'otherservice_reqs']
[docs] def clean(self): # custom validation
cleaned_data = super(WorkorderRepeatForm, self).clean()
# time validation
setup_complete = cleaned_data.get("datetime_setup_complete")
event_start = cleaned_data.get("datetime_start")
event_end = cleaned_data.get("datetime_end")
if not setup_complete or not event_start or not event_end:
raise ValidationError('Please enter in a valid time in each field')
if event_start > event_end:
raise ValidationError('You cannot start after you finish')
if setup_complete > event_start:
raise ValidationError('You cannot setup after you finish')
if setup_complete < datetime.datetime.now(pytz.utc):
raise ValidationError('Stop trying to time travel')
# service exists validation
lighting = cleaned_data.get("lighting", None)
sound = cleaned_data.get("sound", None)
projection = cleaned_data.get("projection", None)
otherservices = cleaned_data.get("otherservices", None)
if not (lighting or sound or projection or not otherservices):
raise ValidationError(
'Please select at least one service, %s %s %s %s' % (lighting, sound, projection, otherservices))
return cleaned_data
[docs]class PostEventSurveyForm(forms.ModelForm):
def __init__(self, event, *args, **kwargs):
self.event = event
self.helper = FormHelper()
self.helper.form_class = "custom-survey-form"
self.helper.layout = Layout(
Div(
Div(
HTML('<div class="striped">'),
'services_quality',
HTML('</div>'),
'lighting_quality',
HTML('<div class="striped">'),
'sound_quality',
HTML('</div>'),
HTML(
'<div class="row" style="padding-top: 1%; padding-bottom: 1%; padding-left: 1%">'
'<div class="col-md-4">'
),
'work_order_method',
HTML('</div></div>'),
HTML('<div style="border-bottom: 2px solid gray; padding-bottom: 1%; margin: 0"></div>'),
HTML('<div id="workorder" style="display: none"><div class="striped">'),
'work_order_experience',
HTML('</div>'),
'work_order_ease',
HTML('<div class="striped" style="padding-bottom: 1%; margin-bottom: 1%">'),
Field('work_order_comments', style='max-width: 75%; margin: 1%;'),
HTML('</div></div>'),
css_class='col'
),
css_class='row'
),
Div(
Div(
HTML('<br><br><h2>Please rate your level of agreement with the following statements.</h2><br>'),
HTML('<div class="striped">'),
'communication_responsiveness',
HTML('</div>'),
'pricelist_ux',
HTML('<div class="striped">'),
'setup_on_time',
HTML('</div>'),
'crew_respectfulness',
HTML('<div class="striped">'),
'price_appropriate',
HTML('</div>'),
'customer_would_return',
HTML('<br>'),
css_class='col'
),
css_class='row'
),
Div(
Div(
HTML('<h2>Additional Comments</h2>'),
'comments',
css_class='col'
),
css_class='row'
),
FormActions(
Submit('save', 'Submit'),
),
)
super(PostEventSurveyForm, self).__init__(*args, **kwargs)
[docs] def save(self, commit=True):
obj = super(PostEventSurveyForm, self).save(commit=False)
obj.event = self.event
if commit:
obj.save()
return obj
[docs] class Meta:
model = PostEventSurvey
fields = ('services_quality', 'lighting_quality', 'sound_quality', 'work_order_method', 'work_order_experience',
'work_order_ease', 'work_order_comments', 'communication_responsiveness', 'pricelist_ux',
'setup_on_time', 'crew_respectfulness', 'price_appropriate', 'customer_would_return', 'comments')
services_quality = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('services_quality').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('services_quality').choices,
)
lighting_quality = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('lighting_quality').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('lighting_quality').choices,
)
sound_quality = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('sound_quality').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('sound_quality').choices,
)
work_order_method = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('work_order_method').verbose_name,
widget=forms.Select,
choices=PostEventSurvey._meta.get_field('work_order_method').choices,
)
work_order_ease = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('work_order_ease').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('work_order_ease').choices,
)
work_order_experience = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('work_order_experience').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('work_order_experience').choices,
)
communication_responsiveness = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('communication_responsiveness').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('communication_responsiveness').choices,
)
pricelist_ux = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('pricelist_ux').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('pricelist_ux').choices,
)
setup_on_time = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('setup_on_time').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('setup_on_time').choices,
)
crew_respectfulness = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('crew_respectfulness').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('crew_respectfulness').choices,
)
price_appropriate = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('price_appropriate').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('price_appropriate').choices,
)
customer_would_return = forms.ChoiceField(
label=PostEventSurvey._meta.get_field('customer_would_return').verbose_name,
widget=SurveyCustomRadioSelect,
choices=PostEventSurvey._meta.get_field('customer_would_return').choices,
)
[docs]class OfficeHoursForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.form_show_labels = False
self.helper.layout = Layout(
Field('day'),
Field('location'),
Field('hour_start'),
Field('hour_end'),
FormActions(
Submit('save', 'Save Changes'),
)
)
super(OfficeHoursForm, self).__init__(*args, **kwargs)
self.fields['location'].queryset = Location.objects.filter(setup_only=True)
[docs]class WorkshopForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal col-md-6"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
Field('name'),
Field('instructors'),
Field('description'),
Field('location'),
Field('notes'),
FormActions(
Submit('save', 'Save')
)
)
super(WorkshopForm, self).__init__(*args, **kwargs)
[docs] class Meta:
model = Workshop
fields = ('name', 'instructors', 'description', 'location', 'notes')
[docs]class WorkshopDatesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal col-md-6"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
Field('workshop'),
Field('date', css_class="form-control"),
FormActions(
Submit('save', 'Save')
)
)
super(WorkshopDatesForm, self).__init__(*args, **kwargs)
date = forms.SplitDateTimeField(required=False)
[docs]class CrewCheckinForm(forms.Form):
def __init__(self, *args, **kwargs):
events = kwargs.pop('events')
title = kwargs.pop('title')
self.helper = FormHelper()
self.helper.form_class = "form-horizontal col-md-6 m-auto"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
HTML('<h2 class="h3">' + title + '</h2><br>'),
Field('event'),
HTML('<hr>'),
FormActions(
Submit('save', 'Submit')
)
)
super(CrewCheckinForm, self).__init__(*args, **kwargs)
options = []
for event in events:
if event.max_crew:
options.append(
(event.pk, " %s (%i spots remaining)" %
(event.event_name, event.max_crew - event.crew_attendance.filter(active=True).count())))
else:
options.append((event.pk, " %s" % event.event_name))
self.fields['event'] = forms.ChoiceField(choices=options, label="Select Event", widget=forms.RadioSelect)
[docs]class CrewCheckoutForm(forms.Form):
checkin = forms.SplitDateTimeField(required=True, label="Verify Checkin Time")
checkout = forms.SplitDateTimeField(required=True, label="Confirm Checkout Time")
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = "form-horizontal container"
self.helper.form_method = "post"
self.helper.form_action = ""
self.helper.layout = Layout(
HTML('<br><h2 class="h3">Does this look correct?</h2><p>Review the checkin and checkout times listed '
'below and verify that they are accurate. Once you submit, you will not be able to edit this '
'information.<br><br></p>'),
Field('checkin', css_class="control mb-2"),
Field('checkout', css_class="control mb-2"),
FormActions(
Submit('save', 'Confirm')
)
)
super(CrewCheckoutForm, self).__init__(*args, **kwargs)
[docs] def clean(self):
cleaned_data = super(CrewCheckoutForm, self).clean()
if cleaned_data['checkout'] > timezone.now():
raise ValidationError('Ha, nice try. Unless you\'ve figured out time travel, you cannot submit a '
'checkout time in the future.')
[docs]class CheckoutHoursForm(forms.Form):
disabled_widget = forms.NumberInput(attrs={'readonly': True, 'step': 0.25})
total = forms.DecimalField(label="Total", decimal_places=2, widget=disabled_widget, required=False)
def __init__(self, *args, **kwargs):
categories = kwargs.pop('categories')
self.total_hrs = kwargs.pop('total_hrs')
self.helper = FormHelper()
self.helper.form_class = "form-horizontal container"
self.helper.form_method = "post"
self.helper.form_action = ""
hour_set = Row(css_class="justify-between")
self.helper.layout = Layout(
HTML('<br><h2 class="h3">Submit Hours</h2><p>If you\'d like to log the hours you have worked, please '
'indicate which services you helped provide.<br><br></p>'),
hour_set,
HTML('<hr>'),
FormActions(
Submit('save', 'Submit')
)
)
super(CheckoutHoursForm, self).__init__(*args, **kwargs)
self.fields['total'].initial = self.total_hrs
for category in categories:
self.fields['hours_%s' % category.name] = forms.DecimalField(label=category.name, required=False,
min_value=0)
hour_set.fields.append(Column('hours_%s' % category.name, css_class="mx-2"))
hour_set.fields.append(Column('total', css_class="mx-2"))
[docs] def clean(self):
cleaned_data = super(CheckoutHoursForm, self).clean()
hour_fields = []
for field in self.fields:
if "hour" in field:
hour_fields.append(field)
total = 0
for field in hour_fields:
if cleaned_data[field]:
total += self.cleaned_data[field]
if total != self.total_hrs and total > 0:
raise ValidationError('Hour totals do not match')
[docs]class BulkCheckinForm(forms.Form):
secure_widget = forms.PasswordInput(attrs={'autofocus': 'autofocus'})
id = forms.IntegerField(required=True, label="Scan / Swipe Student ID", widget=secure_widget)
def __init__(self, *args, **kwargs):
event = kwargs.pop('event')
result = kwargs.pop('result')
user_name = kwargs.pop('user_name')
self.helper = FormHelper()
self.helper.form_class = "form-horizontal col-md-8 m-auto"
self.helper.form_method = 'post'
self.helper.form_action = ""
output = HTML("")
if result == 'checkin-success':
output = HTML("<div class='alert alert-success w-75 m-auto'>Welcome " + user_name + "</div>")
elif result == 'checkin-fail':
output = HTML("<div class='alert alert-danger w-75 m-auto'>Checkin Failed - User is checked into "
"another event</div>")
elif result == 'checkin-limit':
output = HTML("<div class='alert alert-warning w-100 m-auto'>Checkin Failed - This event has reached its "
"crew member or occupancy limit</div>")
elif result == 'checkout-success':
output = HTML("<div class='alert alert-success w-75 m-auto'>Bye. Thanks for your help!</div>")
elif result == 'fail':
output = HTML("<div class='alert alert-danger w-75 m-auto'>Invalid ID</div>")
elif result:
output = HTML("<div class='alert w-75 m-auto' style='background-color: black; color: white'>"
"An unknown error occurred.</div>")
self.helper.layout = Layout(
HTML('<h2 class="h6"><strong>Event:</strong> ' + event + '</h2><br>'),
Field('id'),
FormActions(
Submit('save', 'Enter', css_class="d-none")
),
Div(output, css_class="mt-4 text-center")
)
super(BulkCheckinForm, self).__init__(*args, **kwargs)
# __ __ _ _
# \ \ / /__ _ __| | _____ _ __ __| | ___ _ __
# \ \ /\ / / _ \| '__| |/ / _ \| '__/ _` |/ _ \ '__|
# \ V V / (_) | | | < (_) | | | (_| | __/ |
# \_/\_/ \___/|_| |_|\_\___/|_| \__,_|\___|_|
# _____ _ _
# | ___|__ _ __ _ __ _____ _(_)______ _ _ __ __| |
# | |_ / _ \| '__| '_ ` _ \ \ /\ / / |_ / _` | '__/ _` |
# | _| (_) | | | | | | | \ V V /| |/ / (_| | | | (_| |
# |_| \___/|_| |_| |_| |_|\_/\_/ |_/___\__,_|_| \__,_|
# _____
# | ___|__ _ __ _ __ ___ ___
# | |_ / _ \| '__| '_ ` _ \/ __|
# | _| (_) | | | | | | | \__ \
# |_| \___/|_| |_| |_| |_|___/
SERVICE_INFO_HELP_TEXT = """
Note: Any riders or documentation provided to you from the artist/performer which may help LNL
determine the technical needs of your event may be attached to this request once it is submitted by
going to your LNL account and selecting "Previous Workorders".
"""