|
@@ -15,7 +15,13 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
from django.core.files.storage import default_storage
|
|
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
|
from django.db.models import (
|
|
|
- CharField, TextField, DateTimeField, Model, ForeignKey, PROTECT, CASCADE,
|
|
|
+ CharField,
|
|
|
+ TextField,
|
|
|
+ DateTimeField,
|
|
|
+ Model,
|
|
|
+ ForeignKey,
|
|
|
+ PROTECT,
|
|
|
+ CASCADE,
|
|
|
QuerySet,
|
|
|
)
|
|
|
from django.db.models.fields.files import FieldFile
|
|
@@ -29,7 +35,10 @@ from django.utils.timezone import now
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
from wagtail.models import Page
|
|
|
from wagtail.contrib.forms.models import (
|
|
|
- AbstractForm, AbstractEmailForm, AbstractFormSubmission)
|
|
|
+ AbstractForm,
|
|
|
+ AbstractEmailForm,
|
|
|
+ AbstractFormSubmission,
|
|
|
+)
|
|
|
|
|
|
from .blocks import FormStepBlock, FormFieldBlock
|
|
|
|
|
@@ -38,14 +47,14 @@ class Step:
|
|
|
def __init__(self, steps, index, struct_child):
|
|
|
self.steps = steps
|
|
|
self.index = index
|
|
|
- block = getattr(struct_child, 'block', None)
|
|
|
+ block = getattr(struct_child, "block", None)
|
|
|
if block is None:
|
|
|
struct_child = []
|
|
|
if isinstance(block, FormStepBlock):
|
|
|
- self.name = struct_child.value['name']
|
|
|
- self.form_fields = struct_child.value['form_fields']
|
|
|
+ self.name = struct_child.value["name"]
|
|
|
+ self.form_fields = struct_child.value["form_fields"]
|
|
|
else:
|
|
|
- self.name = ''
|
|
|
+ self.name = ""
|
|
|
self.form_fields = struct_child
|
|
|
|
|
|
@property
|
|
@@ -54,7 +63,7 @@ class Step:
|
|
|
|
|
|
@property
|
|
|
def url(self):
|
|
|
- return '%s?step=%s' % (self.steps.page.url, self.index1)
|
|
|
+ return "%s?step=%s" % (self.steps.page.url, self.index1)
|
|
|
|
|
|
def get_form_fields(self):
|
|
|
form_fields = OrderedDict()
|
|
@@ -68,8 +77,11 @@ class Step:
|
|
|
return form_fields
|
|
|
|
|
|
def get_form_class(self):
|
|
|
- return type('WagtailForm', self.steps.page.get_form_class_bases(),
|
|
|
- self.get_form_fields())
|
|
|
+ return type(
|
|
|
+ "WagtailForm",
|
|
|
+ self.steps.page.get_form_class_bases(),
|
|
|
+ self.get_form_fields(),
|
|
|
+ )
|
|
|
|
|
|
def get_markups_and_bound_fields(self, form):
|
|
|
for struct_child in self.form_fields:
|
|
@@ -77,22 +89,24 @@ class Step:
|
|
|
if isinstance(block, FormFieldBlock):
|
|
|
struct_value = struct_child.value
|
|
|
field_name = block.get_slug(struct_value)
|
|
|
- yield form[field_name], 'field'
|
|
|
+ yield form[field_name], "field"
|
|
|
else:
|
|
|
- yield mark_safe(struct_child), 'markup'
|
|
|
+ yield mark_safe(struct_child), "markup"
|
|
|
|
|
|
def __str__(self):
|
|
|
if self.name:
|
|
|
return self.name
|
|
|
- return _('Step %s') % self.index1
|
|
|
+ return _("Step %s") % self.index1
|
|
|
|
|
|
@property
|
|
|
def badge(self):
|
|
|
- return (mark_safe('<span class="badge">%s/%s</span>')
|
|
|
- % (self.index1, len(self.steps)))
|
|
|
+ return mark_safe('<span class="badge">%s/%s</span>') % (
|
|
|
+ self.index1,
|
|
|
+ len(self.steps),
|
|
|
+ )
|
|
|
|
|
|
def __html__(self):
|
|
|
- return '%s %s' % (self, self.badge)
|
|
|
+ return "%s %s" % (self, self.badge)
|
|
|
|
|
|
@property
|
|
|
def is_active(self):
|
|
@@ -113,23 +127,25 @@ class Step:
|
|
|
@property
|
|
|
def prev(self):
|
|
|
if self.has_prev:
|
|
|
- return self.steps[self.index-1]
|
|
|
+ return self.steps[self.index - 1]
|
|
|
|
|
|
@property
|
|
|
def next(self):
|
|
|
if self.has_next:
|
|
|
- return self.steps[self.index+1]
|
|
|
+ return self.steps[self.index + 1]
|
|
|
|
|
|
def get_existing_data(self, raw=False):
|
|
|
data = self.steps.get_existing_data()[self.index]
|
|
|
fields = self.get_form_fields()
|
|
|
if not raw:
|
|
|
+
|
|
|
class FakeField:
|
|
|
storage = self.steps.get_storage()
|
|
|
|
|
|
for field_name, value in data.items():
|
|
|
- if field_name in fields and isinstance(fields[field_name],
|
|
|
- FileField):
|
|
|
+ if field_name in fields and isinstance(
|
|
|
+ fields[field_name], FileField
|
|
|
+ ):
|
|
|
data[field_name] = FieldFile(None, FakeField, value)
|
|
|
return data
|
|
|
|
|
@@ -157,11 +173,15 @@ class Steps(list):
|
|
|
# TODO: Make it possible to change the `form_fields` attribute.
|
|
|
self.form_fields = page.form_fields
|
|
|
self.request = request
|
|
|
- has_steps = any(isinstance(struct_child.block, FormStepBlock)
|
|
|
- for struct_child in self.form_fields)
|
|
|
+ has_steps = any(
|
|
|
+ isinstance(struct_child.block, FormStepBlock)
|
|
|
+ for struct_child in self.form_fields
|
|
|
+ )
|
|
|
if has_steps:
|
|
|
- steps = [Step(self, i, form_field)
|
|
|
- for i, form_field in enumerate(self.form_fields)]
|
|
|
+ steps = [
|
|
|
+ Step(self, i, form_field)
|
|
|
+ for i, form_field in enumerate(self.form_fields)
|
|
|
+ ]
|
|
|
else:
|
|
|
steps = [Step(self, 0, self.form_fields)]
|
|
|
super().__init__(steps)
|
|
@@ -186,9 +206,10 @@ class Steps(list):
|
|
|
@current.setter
|
|
|
def current(self, new_index: int):
|
|
|
if not isinstance(new_index, int):
|
|
|
- raise TypeError('Use an integer to set the new current step.')
|
|
|
- self.request.session[self.page.current_step_session_key] = \
|
|
|
- self.clamp_index(new_index)
|
|
|
+ raise TypeError("Use an integer to set the new current step.")
|
|
|
+ self.request.session[
|
|
|
+ self.page.current_step_session_key
|
|
|
+ ] = self.clamp_index(new_index)
|
|
|
|
|
|
def forward(self, increment: int = 1):
|
|
|
self.current = self.current_index + increment
|
|
@@ -209,16 +230,19 @@ class Steps(list):
|
|
|
|
|
|
def get_current_form(self):
|
|
|
request = self.request
|
|
|
- if request.method == 'POST':
|
|
|
- step_value = request.POST.get('step', 'next')
|
|
|
- if step_value == 'prev':
|
|
|
+ if request.method == "POST":
|
|
|
+ step_value = request.POST.get("step", "next")
|
|
|
+ if step_value == "prev":
|
|
|
self.backward()
|
|
|
else:
|
|
|
return self.current.get_form_class()(
|
|
|
- request.POST, request.FILES,
|
|
|
- initial=self.current.get_existing_data())
|
|
|
+ request.POST,
|
|
|
+ request.FILES,
|
|
|
+ initial=self.current.get_existing_data(),
|
|
|
+ )
|
|
|
return self.current.get_form_class()(
|
|
|
- initial=self.current.get_existing_data())
|
|
|
+ initial=self.current.get_existing_data()
|
|
|
+ )
|
|
|
|
|
|
def get_storage(self):
|
|
|
return self.page.get_storage()
|
|
@@ -228,21 +252,21 @@ class Steps(list):
|
|
|
for name, field in form.fields.items():
|
|
|
if isinstance(field, FileField):
|
|
|
file = form.cleaned_data[name]
|
|
|
- if file == form.initial.get(name, ''): # Nothing submitted.
|
|
|
+ if file == form.initial.get(name, ""): # Nothing submitted.
|
|
|
form.cleaned_data[name] = file.name
|
|
|
continue
|
|
|
if submission is not None:
|
|
|
submission.delete_file(name)
|
|
|
if not file: # 'Clear' was checked.
|
|
|
- form.cleaned_data[name] = ''
|
|
|
+ form.cleaned_data[name] = ""
|
|
|
continue
|
|
|
directory = self.request.session.session_key
|
|
|
storage = self.get_storage()
|
|
|
- Path(storage.path(directory)).mkdir(parents=True,
|
|
|
- exist_ok=True)
|
|
|
+ Path(storage.path(directory)).mkdir(parents=True, exist_ok=True)
|
|
|
path = storage.get_available_name(
|
|
|
- str(Path(directory) / file.name))
|
|
|
- with storage.open(path, 'wb+') as destination:
|
|
|
+ str(Path(directory) / file.name)
|
|
|
+ )
|
|
|
+ with storage.open(path, "wb+") as destination:
|
|
|
for chunk in file.chunks():
|
|
|
destination.write(chunk)
|
|
|
form.cleaned_data[name] = path
|
|
@@ -271,29 +295,33 @@ class Steps(list):
|
|
|
class SessionFormSubmission(AbstractFormSubmission):
|
|
|
|
|
|
session_key = CharField(max_length=40, null=True, default=None)
|
|
|
- user = ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
|
|
|
- related_name='+', on_delete=PROTECT)
|
|
|
+ user = ForeignKey(
|
|
|
+ settings.AUTH_USER_MODEL,
|
|
|
+ null=True,
|
|
|
+ blank=True,
|
|
|
+ related_name="+",
|
|
|
+ on_delete=PROTECT,
|
|
|
+ )
|
|
|
thumbnails_by_path = TextField(default=json.dumps({}))
|
|
|
- last_modification = DateTimeField(_('last modification'), auto_now=True)
|
|
|
- INCOMPLETE = 'incomplete'
|
|
|
- COMPLETE = 'complete'
|
|
|
- REVIEWED = 'reviewed'
|
|
|
- APPROVED = 'approved'
|
|
|
- REJECTED = 'rejected'
|
|
|
+ last_modification = DateTimeField(_("last modification"), auto_now=True)
|
|
|
+ INCOMPLETE = "incomplete"
|
|
|
+ COMPLETE = "complete"
|
|
|
+ REVIEWED = "reviewed"
|
|
|
+ APPROVED = "approved"
|
|
|
+ REJECTED = "rejected"
|
|
|
STATUSES = (
|
|
|
- (INCOMPLETE, _('Not submitted')),
|
|
|
- (COMPLETE, _('In progress')),
|
|
|
- (REVIEWED, _('Under consideration')),
|
|
|
- (APPROVED, _('Approved')),
|
|
|
- (REJECTED, _('Rejected')),
|
|
|
+ (INCOMPLETE, _("Not submitted")),
|
|
|
+ (COMPLETE, _("In progress")),
|
|
|
+ (REVIEWED, _("Under consideration")),
|
|
|
+ (APPROVED, _("Approved")),
|
|
|
+ (REJECTED, _("Rejected")),
|
|
|
)
|
|
|
status = CharField(max_length=10, choices=STATUSES, default=INCOMPLETE)
|
|
|
|
|
|
class Meta:
|
|
|
- verbose_name = _('form submission')
|
|
|
- verbose_name_plural = _('form submissions')
|
|
|
- unique_together = (('page', 'session_key'),
|
|
|
- ('page', 'user'))
|
|
|
+ verbose_name = _("form submission")
|
|
|
+ verbose_name_plural = _("form submissions")
|
|
|
+ unique_together = (("page", "session_key"), ("page", "user"))
|
|
|
abstract = True
|
|
|
|
|
|
@property
|
|
@@ -306,7 +334,8 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
|
|
|
def get_session(self):
|
|
|
return import_module(settings.SESSION_ENGINE).SessionStore(
|
|
|
- session_key=self.session_key)
|
|
|
+ session_key=self.session_key
|
|
|
+ )
|
|
|
|
|
|
def reset_step(self):
|
|
|
session = self.get_session()
|
|
@@ -322,8 +351,8 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
|
|
|
def get_thumbnail_path(self, path, width=64, height=64):
|
|
|
if not path:
|
|
|
- return ''
|
|
|
- variant = '%s×%s' % (width, height)
|
|
|
+ return ""
|
|
|
+ variant = "%s×%s" % (width, height)
|
|
|
thumbnails_by_path = json.loads(self.thumbnails_by_path)
|
|
|
thumbnails_paths = thumbnails_by_path.get(path)
|
|
|
if thumbnails_paths is None:
|
|
@@ -334,8 +363,7 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
return thumbnail_path
|
|
|
|
|
|
path = Path(path)
|
|
|
- thumbnail_path = str(path.with_suffix('.%s%s'
|
|
|
- % (variant, path.suffix)))
|
|
|
+ thumbnail_path = str(path.with_suffix(".%s%s" % (variant, path.suffix)))
|
|
|
storage = self.get_storage()
|
|
|
thumbnail_path = storage.get_available_name(thumbnail_path)
|
|
|
|
|
@@ -344,8 +372,9 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
thumbnail.save(storage.path(thumbnail_path))
|
|
|
|
|
|
thumbnails_by_path[str(path)][variant] = thumbnail_path
|
|
|
- self.thumbnails_by_path = json.dumps(thumbnails_by_path,
|
|
|
- cls=StreamFormJSONEncoder)
|
|
|
+ self.thumbnails_by_path = json.dumps(
|
|
|
+ thumbnails_by_path, cls=StreamFormJSONEncoder
|
|
|
+ )
|
|
|
self.save()
|
|
|
return thumbnail_path
|
|
|
|
|
@@ -365,7 +394,8 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
path = data.get(name)
|
|
|
if path:
|
|
|
files[name] = [path] + list(
|
|
|
- self.get_existing_thumbnails(path))
|
|
|
+ self.get_existing_thumbnails(path)
|
|
|
+ )
|
|
|
return files
|
|
|
|
|
|
def get_all_files(self):
|
|
@@ -379,43 +409,44 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
self.get_storage().delete(path)
|
|
|
if path in thumbnails_by_path:
|
|
|
del thumbnails_by_path[path]
|
|
|
- self.thumbnails_by_path = json.dumps(thumbnails_by_path,
|
|
|
- cls=StreamFormJSONEncoder)
|
|
|
+ self.thumbnails_by_path = json.dumps(
|
|
|
+ thumbnails_by_path, cls=StreamFormJSONEncoder
|
|
|
+ )
|
|
|
self.save()
|
|
|
|
|
|
def render_email(self, value):
|
|
|
- return (mark_safe('<a href="mailto:%s" target="_blank">%s</a>')
|
|
|
- % (value, value))
|
|
|
+ return mark_safe('<a href="mailto:%s" target="_blank">%s</a>') % (
|
|
|
+ value,
|
|
|
+ value,
|
|
|
+ )
|
|
|
|
|
|
def render_link(self, value):
|
|
|
- return (mark_safe('<a href="%s" target="_blank">%s</a>')
|
|
|
- % (value, value))
|
|
|
+ return mark_safe('<a href="%s" target="_blank">%s</a>') % (value, value)
|
|
|
|
|
|
def render_image(self, value):
|
|
|
storage = self.get_storage()
|
|
|
- return (mark_safe('<a href="%s" target="_blank"><img src="%s" /></a>')
|
|
|
- % (storage.url(value),
|
|
|
- storage.url(self.get_thumbnail_path(value))))
|
|
|
+ return mark_safe(
|
|
|
+ '<a href="%s" target="_blank"><img src="%s" /></a>'
|
|
|
+ ) % (storage.url(value), storage.url(self.get_thumbnail_path(value)))
|
|
|
|
|
|
def render_file(self, value):
|
|
|
return mark_safe('<a href="%s" target="_blank">%s</a>') % (
|
|
|
self.get_storage().url(value),
|
|
|
- Path(value).name
|
|
|
+ Path(value).name,
|
|
|
)
|
|
|
|
|
|
def format_value(self, field, value):
|
|
|
- if value is None or value == '':
|
|
|
- return '-'
|
|
|
+ if value is None or value == "":
|
|
|
+ return "-"
|
|
|
new_value = self.form_page.format_value(field, value)
|
|
|
if new_value != value:
|
|
|
return new_value
|
|
|
if value is True:
|
|
|
- return 'Yes'
|
|
|
+ return "Yes"
|
|
|
if value is False:
|
|
|
- return 'No'
|
|
|
+ return "No"
|
|
|
if isinstance(value, (list, tuple)):
|
|
|
- return ', '.join([self.format_value(field, item)
|
|
|
- for item in value])
|
|
|
+ return ", ".join([self.format_value(field, item) for item in value])
|
|
|
if isinstance(value, datetime.date):
|
|
|
return value
|
|
|
if isinstance(field, EmailField):
|
|
@@ -426,30 +457,37 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
return self.render_image(value)
|
|
|
if isinstance(field, FileField):
|
|
|
return self.render_file(value)
|
|
|
- if isinstance(value, SafeData) or hasattr(value, '__html__'):
|
|
|
+ if isinstance(value, SafeData) or hasattr(value, "__html__"):
|
|
|
return value
|
|
|
return str(value)
|
|
|
|
|
|
def format_db_field(self, field_name, raw=False):
|
|
|
- method = getattr(self, 'get_%s_display' % field_name, None)
|
|
|
+ method = getattr(self, "get_%s_display" % field_name, None)
|
|
|
if method is not None:
|
|
|
return method()
|
|
|
value = getattr(self, field_name)
|
|
|
if raw:
|
|
|
return value
|
|
|
- return self.format_value(self._meta.get_field(field_name).formfield(),
|
|
|
- value)
|
|
|
+ return self.format_value(
|
|
|
+ self._meta.get_field(field_name).formfield(), value
|
|
|
+ )
|
|
|
|
|
|
def get_steps_data(self, raw=False):
|
|
|
steps_data = json.loads(self.form_data)
|
|
|
if raw:
|
|
|
return steps_data
|
|
|
- fields_and_data_iterator = zip_longest(self.get_fields(by_step=True),
|
|
|
- steps_data, fillvalue={})
|
|
|
+ fields_and_data_iterator = zip_longest(
|
|
|
+ self.get_fields(by_step=True), steps_data, fillvalue={}
|
|
|
+ )
|
|
|
return [
|
|
|
- OrderedDict([(name, self.format_value(field, step_data.get(name)))
|
|
|
- for name, field in step_fields.items()])
|
|
|
- for step_fields, step_data in fields_and_data_iterator]
|
|
|
+ OrderedDict(
|
|
|
+ [
|
|
|
+ (name, self.format_value(field, step_data.get(name)))
|
|
|
+ for name, field in step_fields.items()
|
|
|
+ ]
|
|
|
+ )
|
|
|
+ for step_fields, step_data in fields_and_data_iterator
|
|
|
+ ]
|
|
|
|
|
|
def get_extra_data(self, raw=False):
|
|
|
return self.form_page.get_extra_data(self, raw=raw)
|
|
@@ -462,26 +500,30 @@ class SessionFormSubmission(AbstractFormSubmission):
|
|
|
form_data.update(step_data)
|
|
|
if add_metadata:
|
|
|
form_data.update(
|
|
|
- status=self.format_db_field('status', raw=raw),
|
|
|
- user=self.format_db_field('user', raw=raw),
|
|
|
- submit_time=self.format_db_field('submit_time', raw=raw),
|
|
|
- last_modification=self.format_db_field('last_modification',
|
|
|
- raw=raw),
|
|
|
+ status=self.format_db_field("status", raw=raw),
|
|
|
+ user=self.format_db_field("user", raw=raw),
|
|
|
+ submit_time=self.format_db_field("submit_time", raw=raw),
|
|
|
+ last_modification=self.format_db_field(
|
|
|
+ "last_modification", raw=raw
|
|
|
+ ),
|
|
|
)
|
|
|
return form_data
|
|
|
|
|
|
def steps_with_data_iterator(self, raw=False):
|
|
|
for step, step_data_fields, step_data in zip(
|
|
|
- self.form_page.get_steps(),
|
|
|
- self.form_page.get_data_fields(by_step=True),
|
|
|
- self.get_steps_data(raw=raw)):
|
|
|
- yield step, [(field_name, field_label, step_data[field_name])
|
|
|
- for field_name, field_label in step_data_fields]
|
|
|
+ self.form_page.get_steps(),
|
|
|
+ self.form_page.get_data_fields(by_step=True),
|
|
|
+ self.get_steps_data(raw=raw),
|
|
|
+ ):
|
|
|
+ yield step, [
|
|
|
+ (field_name, field_label, step_data[field_name])
|
|
|
+ for field_name, field_label in step_data_fields
|
|
|
+ ]
|
|
|
|
|
|
|
|
|
@receiver(post_delete, sender=SessionFormSubmission)
|
|
|
def delete_files(sender, **kwargs):
|
|
|
- instance = kwargs['instance']
|
|
|
+ instance = kwargs["instance"]
|
|
|
instance.reset_step()
|
|
|
storage = instance.get_storage()
|
|
|
for path in instance.get_all_files():
|
|
@@ -514,34 +556,35 @@ class SubmissionRevisionQuerySet(QuerySet):
|
|
|
|
|
|
|
|
|
class SubmissionRevision(Model):
|
|
|
- CREATED = 'created'
|
|
|
- CHANGED = 'changed'
|
|
|
- DELETED = 'deleted'
|
|
|
+ CREATED = "created"
|
|
|
+ CHANGED = "changed"
|
|
|
+ DELETED = "deleted"
|
|
|
TYPES = (
|
|
|
- (CREATED, _('Created')),
|
|
|
- (CHANGED, _('Changed')),
|
|
|
- (DELETED, _('Deleted')),
|
|
|
+ (CREATED, _("Created")),
|
|
|
+ (CHANGED, _("Changed")),
|
|
|
+ (DELETED, _("Deleted")),
|
|
|
)
|
|
|
type = CharField(max_length=7, choices=TYPES)
|
|
|
created_at = DateTimeField(auto_now_add=True)
|
|
|
- submission_ct = ForeignKey('contenttypes.ContentType', on_delete=CASCADE)
|
|
|
+ submission_ct = ForeignKey("contenttypes.ContentType", on_delete=CASCADE)
|
|
|
submission_id = TextField()
|
|
|
- submission = GenericForeignKey('submission_ct', 'submission_id')
|
|
|
+ submission = GenericForeignKey("submission_ct", "submission_id")
|
|
|
data = TextField()
|
|
|
summary = TextField()
|
|
|
|
|
|
objects = SubmissionRevisionQuerySet.as_manager()
|
|
|
|
|
|
class Meta:
|
|
|
- ordering = ('-created_at',)
|
|
|
+ ordering = ("-created_at",)
|
|
|
abstract = True
|
|
|
|
|
|
@staticmethod
|
|
|
def get_filters_for(submission):
|
|
|
return {
|
|
|
- 'submission_ct':
|
|
|
- ContentType.objects.get_for_model(submission._meta.model),
|
|
|
- 'submission_id': str(submission.pk),
|
|
|
+ "submission_ct": ContentType.objects.get_for_model(
|
|
|
+ submission._meta.model
|
|
|
+ ),
|
|
|
+ "submission_id": str(submission.pk),
|
|
|
}
|
|
|
|
|
|
@classmethod
|
|
@@ -554,56 +597,72 @@ class SubmissionRevision(Model):
|
|
|
value2 = data2.get(k)
|
|
|
if value2 == value1 or not value1 and not value2:
|
|
|
continue
|
|
|
- is_hidden = (isinstance(value1, hidden_types)
|
|
|
- or isinstance(value2, hidden_types))
|
|
|
+ is_hidden = isinstance(value1, hidden_types) or isinstance(
|
|
|
+ value2, hidden_types
|
|
|
+ )
|
|
|
|
|
|
# Escapes newlines as they are used as separator inside summaries.
|
|
|
if isinstance(value1, str):
|
|
|
- value1 = value1.replace('\n', r'\n')
|
|
|
+ value1 = value1.replace("\n", r"\n")
|
|
|
if isinstance(value2, str):
|
|
|
- value2 = value2.replace('\n', r'\n')
|
|
|
+ value2 = value2.replace("\n", r"\n")
|
|
|
|
|
|
if value2 and not value1:
|
|
|
diff.append(
|
|
|
- ((_('“%s” set.') % label) if is_hidden
|
|
|
- else (_('“%s” set to “%s”.')) % (label, value2)))
|
|
|
+ (
|
|
|
+ (_("“%s” set.") % label)
|
|
|
+ if is_hidden
|
|
|
+ else (_("“%s” set to “%s”.")) % (label, value2)
|
|
|
+ )
|
|
|
+ )
|
|
|
elif value1 and not value2:
|
|
|
- diff.append(_('“%s” unset.') % label)
|
|
|
+ diff.append(_("“%s” unset.") % label)
|
|
|
else:
|
|
|
- diff.append(((_('“%s” changed.') % label) if is_hidden
|
|
|
- else (_('“%s” changed from “%s” to “%s”.')
|
|
|
- % (label, value1, value2))))
|
|
|
- return '\n'.join(diff)
|
|
|
+ diff.append(
|
|
|
+ (
|
|
|
+ (_("“%s” changed.") % label)
|
|
|
+ if is_hidden
|
|
|
+ else (
|
|
|
+ _("“%s” changed from “%s” to “%s”.")
|
|
|
+ % (label, value1, value2)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return "\n".join(diff)
|
|
|
|
|
|
@classmethod
|
|
|
def create_from_submission(cls, submission, revision_type):
|
|
|
page = submission.form_page
|
|
|
try:
|
|
|
- previous = cls.objects.for_submission(
|
|
|
- submission).latest('created_at')
|
|
|
+ previous = cls.objects.for_submission(submission).latest(
|
|
|
+ "created_at"
|
|
|
+ )
|
|
|
except cls.DoesNotExist:
|
|
|
previous_data = {}
|
|
|
else:
|
|
|
previous_data = previous.get_data()
|
|
|
filters = cls.get_filters_for(submission)
|
|
|
data = submission.get_data(raw=True, add_metadata=False)
|
|
|
- data['status'] = submission.status
|
|
|
+ data["status"] = submission.status
|
|
|
if revision_type == cls.CREATED:
|
|
|
- summary = _('Submission created.')
|
|
|
+ summary = _("Submission created.")
|
|
|
elif revision_type == cls.DELETED:
|
|
|
- summary = _('Submission deleted.')
|
|
|
+ summary = _("Submission deleted.")
|
|
|
else:
|
|
|
summary = cls.diff_summary(page, previous_data, data)
|
|
|
if not summary: # Nothing changed.
|
|
|
return
|
|
|
filters.update(
|
|
|
- type=revision_type, data=json.dumps(data, cls=StreamFormJSONEncoder), summary=summary
|
|
|
+ type=revision_type,
|
|
|
+ data=json.dumps(data, cls=StreamFormJSONEncoder),
|
|
|
+ summary=summary,
|
|
|
)
|
|
|
return cls.objects.create(**filters)
|
|
|
|
|
|
def get_data(self):
|
|
|
return json.loads(self.data)
|
|
|
|
|
|
+
|
|
|
# ORIGINAL NORIPYT CODE.
|
|
|
# We don't want these receivers triggering.
|
|
|
|
|
@@ -632,10 +691,10 @@ class StreamFormMixin:
|
|
|
|
|
|
@property
|
|
|
def current_step_session_key(self):
|
|
|
- return '%s:step' % self.pk
|
|
|
+ return "%s:step" % self.pk
|
|
|
|
|
|
def get_steps(self, request=None):
|
|
|
- if not hasattr(self, 'steps'):
|
|
|
+ if not hasattr(self, "steps"):
|
|
|
steps = Steps(self, request=request)
|
|
|
if request is None:
|
|
|
return steps
|
|
@@ -653,7 +712,7 @@ class StreamFormMixin:
|
|
|
def get_context(self, request, *args, **kwargs):
|
|
|
context = super().get_context(request, *args, **kwargs)
|
|
|
self.steps = self.get_steps(request)
|
|
|
- step_value = request.GET.get('step')
|
|
|
+ step_value = request.GET.get("step")
|
|
|
if step_value is not None and step_value.isdigit():
|
|
|
self.steps.current = int(step_value) - 1
|
|
|
form = self.steps.get_current_form()
|
|
@@ -662,7 +721,8 @@ class StreamFormMixin:
|
|
|
step=self.steps.current,
|
|
|
form=form,
|
|
|
markups_and_bound_fields=list(
|
|
|
- self.steps.current.get_markups_and_bound_fields(form)),
|
|
|
+ self.steps.current.get_markups_and_bound_fields(form)
|
|
|
+ ),
|
|
|
)
|
|
|
return context
|
|
|
|
|
@@ -671,7 +731,7 @@ class StreamFormMixin:
|
|
|
|
|
|
@staticmethod
|
|
|
def get_form_class_bases():
|
|
|
- return Form,
|
|
|
+ return (Form,)
|
|
|
|
|
|
@staticmethod
|
|
|
def get_submission_class():
|
|
@@ -680,25 +740,37 @@ class StreamFormMixin:
|
|
|
def get_submission(self, request):
|
|
|
Submission = self.get_submission_class()
|
|
|
if request.user.is_authenticated:
|
|
|
- user_submission = Submission.objects.filter(
|
|
|
- user=request.user, page=self).order_by('-pk').first()
|
|
|
+ user_submission = (
|
|
|
+ Submission.objects.filter(user=request.user, page=self)
|
|
|
+ .order_by("-pk")
|
|
|
+ .first()
|
|
|
+ )
|
|
|
if user_submission is None:
|
|
|
- return Submission(user=request.user, page=self, form_data='[]')
|
|
|
+ return Submission(user=request.user, page=self, form_data="[]")
|
|
|
return user_submission
|
|
|
|
|
|
- user_submission = Submission.objects.filter(
|
|
|
- session_key=request.session.session_key, page=self
|
|
|
- ).order_by('-pk').first()
|
|
|
+ user_submission = (
|
|
|
+ Submission.objects.filter(
|
|
|
+ session_key=request.session.session_key, page=self
|
|
|
+ )
|
|
|
+ .order_by("-pk")
|
|
|
+ .first()
|
|
|
+ )
|
|
|
if user_submission is None:
|
|
|
- return Submission(session_key=request.session.session_key,
|
|
|
- page=self, form_data='[]')
|
|
|
+ return Submission(
|
|
|
+ session_key=request.session.session_key,
|
|
|
+ page=self,
|
|
|
+ form_data="[]",
|
|
|
+ )
|
|
|
return user_submission
|
|
|
|
|
|
def get_success_url(self):
|
|
|
- form_complete_models = [model for model in apps.get_models()
|
|
|
- if issubclass(model, FormCompleteMixin)]
|
|
|
- cts = (ContentType.objects
|
|
|
- .get_for_models(*form_complete_models).values())
|
|
|
+ form_complete_models = [
|
|
|
+ model
|
|
|
+ for model in apps.get_models()
|
|
|
+ if issubclass(model, FormCompleteMixin)
|
|
|
+ ]
|
|
|
+ cts = ContentType.objects.get_for_models(*form_complete_models).values()
|
|
|
first_child = self.get_children().filter(content_type__in=cts).first()
|
|
|
if first_child is None:
|
|
|
return self.url
|
|
@@ -707,14 +779,13 @@ class StreamFormMixin:
|
|
|
def serve_success(self, request, *args, **kwargs):
|
|
|
url = self.get_success_url()
|
|
|
if url == self.url:
|
|
|
- messages.success(request,
|
|
|
- _('Successfully submitted the form.'))
|
|
|
+ messages.success(request, _("Successfully submitted the form."))
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
def serve(self, request, *args, **kwargs):
|
|
|
context = self.get_context(request)
|
|
|
- form = context['form']
|
|
|
- if request.method == 'POST' and form.is_valid():
|
|
|
+ form = context["form"]
|
|
|
+ if request.method == "POST" and form.is_valid():
|
|
|
is_complete = self.steps.update_data()
|
|
|
if is_complete:
|
|
|
return self.serve_success(request, *args, **kwargs)
|
|
@@ -723,22 +794,32 @@ class StreamFormMixin:
|
|
|
|
|
|
def get_data_fields(self, by_step=False, add_metadata=True):
|
|
|
if by_step:
|
|
|
- return [[(field_name, field.label)
|
|
|
- for field_name, field in step_fields.items()]
|
|
|
- for step_fields in self.get_form_fields(by_step=True)]
|
|
|
+ return [
|
|
|
+ [
|
|
|
+ (field_name, field.label)
|
|
|
+ for field_name, field in step_fields.items()
|
|
|
+ ]
|
|
|
+ for step_fields in self.get_form_fields(by_step=True)
|
|
|
+ ]
|
|
|
|
|
|
data_fields = []
|
|
|
data_fields.extend(self.get_extra_data_fields())
|
|
|
if add_metadata:
|
|
|
- data_fields.extend((
|
|
|
- ('status', _('Status')),
|
|
|
- ('user', _('User')),
|
|
|
- ('submit_time', _('First modification')),
|
|
|
- ('last_modification', _('Last modification'))))
|
|
|
- data_fields.extend([
|
|
|
- (field_name, field_label)
|
|
|
- for step_data_fields in self.get_data_fields(by_step=True)
|
|
|
- for field_name, field_label in step_data_fields])
|
|
|
+ data_fields.extend(
|
|
|
+ (
|
|
|
+ ("status", _("Status")),
|
|
|
+ ("user", _("User")),
|
|
|
+ ("submit_time", _("First modification")),
|
|
|
+ ("last_modification", _("Last modification")),
|
|
|
+ )
|
|
|
+ )
|
|
|
+ data_fields.extend(
|
|
|
+ [
|
|
|
+ (field_name, field_label)
|
|
|
+ for step_data_fields in self.get_data_fields(by_step=True)
|
|
|
+ for field_name, field_label in step_data_fields
|
|
|
+ ]
|
|
|
+ )
|
|
|
return data_fields
|
|
|
|
|
|
def get_extra_data_fields(self):
|
|
@@ -767,7 +848,7 @@ class ClosingFormMixin(Model):
|
|
|
if self.closed_template is None:
|
|
|
template = self.get_template(request, *args, **kwargs)
|
|
|
base, ext = os.path.splitext(template)
|
|
|
- return '%s_closed%s' % (base, ext)
|
|
|
+ return "%s_closed%s" % (base, ext)
|
|
|
return self.closed_template
|
|
|
|
|
|
def serve_closed(self, request, *args, **kwargs):
|
|
@@ -789,19 +870,24 @@ class FormCompleteMixin:
|
|
|
|
|
|
def serve(self, request, *args, **kwargs):
|
|
|
form_page = self.get_form_page()
|
|
|
- if isinstance(form_page, LoginRequiredMixin) \
|
|
|
- and not request.user.is_authenticated():
|
|
|
+ if (
|
|
|
+ isinstance(form_page, LoginRequiredMixin)
|
|
|
+ and not request.user.is_authenticated()
|
|
|
+ ):
|
|
|
return HttpResponseRedirect(form_page.url)
|
|
|
self.submission = form_page.get_submission(request)
|
|
|
- if self.submission is not None and self.submission.is_complete \
|
|
|
- or getattr(request, 'is_preview', False):
|
|
|
+ if (
|
|
|
+ self.submission is not None
|
|
|
+ and self.submission.is_complete
|
|
|
+ or getattr(request, "is_preview", False)
|
|
|
+ ):
|
|
|
return super().serve(request, *args, **kwargs)
|
|
|
return HttpResponseRedirect(form_page.url)
|
|
|
|
|
|
def get_context(self, *args, **kwargs):
|
|
|
context = super().get_context(*args, **kwargs)
|
|
|
- if hasattr(self, 'submission'):
|
|
|
- context['submission'] = self.submission
|
|
|
+ if hasattr(self, "submission"):
|
|
|
+ context["submission"] = self.submission
|
|
|
return context
|
|
|
|
|
|
|
|
@@ -812,7 +898,7 @@ class LoginRequiredMixin:
|
|
|
if self.login_required_template is None:
|
|
|
template = self.get_template(request, *args, **kwargs)
|
|
|
base, ext = os.path.splitext(template)
|
|
|
- return '%s_login_required%s' % (base, ext)
|
|
|
+ return "%s_login_required%s" % (base, ext)
|
|
|
return self.login_required_template
|
|
|
|
|
|
def serve_login_required(self, request, *args, **kwargs):
|