Newer
Older
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import JSONField
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
from django.core.files.storage import default_storage
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q
from django.db.models.expressions import RawSQL, OrderBy
from django.http import Http404
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.text import mark_safe, slugify
from django.utils.translation import ugettext_lazy as _
from django_fsm import can_proceed, FSMField, transition, RETURN_VALUE
from modelcluster.fields import ParentalKey, ParentalManyToManyField
from wagtail.admin.edit_handlers import (
from wagtail.admin.utils import send_mail
from wagtail.core.fields import StreamField
from wagtail.core.models import Orderable
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormSubmission
from opentech.apply.activity.messaging import messenger, MESSAGES
from opentech.apply.stream_forms.blocks import UploadableMediaBlock
from opentech.apply.stream_forms.models import AbstractStreamForm, BaseStreamForm
from opentech.apply.users.groups import REVIEWER_GROUP_NAME, STAFF_GROUP_NAME
from opentech.apply.utils.blocks import MustIncludeFieldBlock
from .admin_forms import WorkflowFormAdminForm
from .blocks import ApplicationCustomFormFieldsBlock, REQUIRED_BLOCK_NAMES
from .edit_handlers import FilteredFieldPanel, ReadOnlyPanel, ReadOnlyInlinePanel
from .workflow import (
active_statuses,
get_review_statuses,
INITIAL_STATE,
review_statuses,
UserPermissions,
WORKFLOWS,
DETERMINATION_RESPONSE_PHASES,
LIMIT_TO_STAFF = {'groups__name': STAFF_GROUP_NAME}
LIMIT_TO_REVIEWERS = {'groups__name': REVIEWER_GROUP_NAME}
LIMIT_TO_STAFF_AND_REVIEWERS = {'groups__name__in': [STAFF_GROUP_NAME, REVIEWER_GROUP_NAME]}
def admin_url(page):
return reverse('wagtailadmin_pages:edit', args=(page.id,))
class SubmittableStreamForm(AbstractStreamForm):
"""
Controls how stream forms are submitted. Any Page allowing submissions should inherit from here.
"""
class Meta:
abstract = True
def get_submission_class(self):
return ApplicationSubmission
def process_form_submission(self, form):
if not form.user.is_authenticated:
return self.get_submission_class().objects.create(
form_data=form.cleaned_data,
form_fields=self.get_defined_fields(),
**self.get_submit_meta_data(user=form.user),
)
def get_submit_meta_data(self, **kwargs):
return kwargs
class WorkflowHelpers(models.Model):
"""
Defines the common methods and fields for working with Workflows within Django models
"""
class Meta:
abstract = True
WORKFLOW_CHOICES = {
name: workflow.name
for name, workflow in WORKFLOWS.items()
workflow_name = models.CharField(choices=WORKFLOW_CHOICES.items(), max_length=100, default='single', verbose_name="Workflow")
@property
def workflow(self):
return WORKFLOWS[self.workflow_name]
class WorkflowStreamForm(WorkflowHelpers, AbstractStreamForm): # type: ignore
"""
Defines the common methods and fields for working with Workflows within Wagtail pages
"""
class Meta:
abstract = True
def get_defined_fields(self, stage=None):
if not stage:
form_index = 0
else:
form_index = self.workflow.stages.index(stage)
return self.forms.all()[form_index].fields
def render_landing_page(self, request, form_submission=None, *args, **kwargs):
# We only reach this page after creation of a new submission
# Hook in to notify about new applications
messenger(
MESSAGES.NEW_SUBMISSION,
request=request,
user=form_submission.user,
submission=form_submission,
)
return super().render_landing_page(request, form_submission=None, *args, **kwargs)
content_panels = AbstractStreamForm.content_panels + [
FieldPanel('workflow_name'),
InlinePanel('forms', label="Forms"),
InlinePanel('review_forms', label="Review Forms")
]
class EmailForm(AbstractEmailForm):
"""
Defines the behaviour for pages that hold information about emailing applicants
Email Confirmation Panel should be included to allow admins to make changes.
"""
class Meta:
abstract = True
confirmation_text_extra = models.TextField(blank=True, help_text="Additional text for the application confirmation message.")
def process_form_submission(self, form):
submission = super().process_form_submission(form)
self.send_mail(submission)
return submission
def send_mail(self, submission):
user = submission.user
'name': user.get_full_name(),
'email': user.email,
'project_name': submission.form_data.get('title'),
'extra_text': self.confirmation_text_extra,
'fund_type': self.title,
}
subject = self.subject if self.subject else 'Thank you for your submission to Open Technology Fund'
send_mail(subject, render_to_string('funds/email/confirmation.txt', context), (user.email,), self.from_address, )
MultiFieldPanel(
[
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
FieldPanel('confirmation_text_extra'),
],
heading="Confirmation email",
email_tab = ObjectList(email_confirmation_panels, heading='Confirmation email')
Todd Dembrey
committed
class FundType(EmailForm, WorkflowStreamForm): # type: ignore
class Meta:
verbose_name = _("Fund")
# Adds validation around forms & workflows. Isn't on Workflow class due to not displaying workflow field on Round
base_form_class = WorkflowFormAdminForm
reviewers = ParentalManyToManyField(
settings.AUTH_USER_MODEL,
related_name='fund_reviewers',
limit_choices_to=LIMIT_TO_REVIEWERS,
Loading
Loading full blame...