<commit_before>from __future__ import unicode_literals

import contextlib

from django.test import TestCase
from django.test.client import RequestFactory
from django import forms
from django.utils import timezone
from django.core import mail
from django.utils import unittest

import mock

from modeltests.core_tests.widgy_config import widgy_site
from widgy.contrib.form_builder.models import (
    Form, FormInput, Textarea, FormSubmission, FormField, Uncaptcha,
    EmailUserHandler, EmailSuccessHandler
)
from widgy.exceptions import ParentChildRejection
from widgy.utils import build_url
from widgy.models import VersionTracker


class GetFormTest(TestCase):
    def setUp(self):
        self.form = Form.add_root(widgy_site)

    def test_get_form(self):
        input1 = self.form.children['fields'].add_child(widgy_site, FormInput)
        input1.type = 'text'
        input1.label = 'Test'
        input1.save()

        input2 = self.form.children['fields'].add_child(widgy_site, FormInput)
        input2.type = 'text'
        input2.label = 'Test 2'
        input2.save()

        form_class = self.form.build_form_class()

        data = {
            input1.get_formfield_name(): 'foo',
            input2.get_formfield_name(): 'bar',
        }

        f = form_class(data)
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, data)

    def test_get_formfield(self):
        self.form = Form.add_root(widgy_site)

        input = self.form.children['fields'].add_child(widgy_site, FormInput)
        input.type = 'text'
        input.label = 'Test'
        input.save()

        field = input.get_formfield()

        self.assertTrue(isinstance(field, forms.CharField))
        self.assertEqual(field.label, 'Test')

    def test_with_uncaptcha(self):
        uncaptcha = self.form.children['fields'].add_child(widgy_site, Uncaptcha)
        form_class = self.form.build_form_class()
        self.assertTrue(hasattr(form_class, 'clean_%s' % uncaptcha.get_formfield_name()))


@contextlib.contextmanager
def mock_now():
    now = timezone.now()
    with mock.patch('django.utils.timezone.now') as tz_now:
        tz_now.return_value = now
        yield now


class TestForm(TestCase):
    def make_form(self):
        form = Form.add_root(widgy_site)
        fields = []
        fields.append(form.children['fields'].add_child(widgy_site, FormInput,
                                                        label='field 1',
                                                        type='text'))
        fields.append(form.children['fields'].add_child(widgy_site, FormInput,
                                                        label='field 2',
                                                        type='text'))
        fields.append(form.children['fields'].add_child(widgy_site, Textarea,
                                                        label='field 3',))
        return form, fields

    def setUp(self):
        self.form, self.fields = self.make_form()

    def submit(self, a, b, c, form=None):
        form = form or self.form
        fields = [i for i in form.children['fields'].get_children() if isinstance(i, FormField)]

        return FormSubmission.objects.submit(
            form=form or self.form,
            data={
                fields[0].get_formfield_name(): a,
                fields[1].get_formfield_name(): b,
                fields[2].get_formfield_name(): c,
            })

    def test_delete_doesnt_delete(self):
        form = Form.add_root(widgy_site)
        input = form.children['fields'].add_child(widgy_site, FormInput)

        form.delete()

        form = Form.objects.get(pk=form.pk)
        self.assertTrue(form.node.is_root())
        self.assertIn(input, form.children['fields'].get_children())

    def test_as_dict(self):
        with mock_now() as now:
            submission = self.submit('a', 'b', 'c')
        expected = {
            'created_at': now,
            self.fields[0].ident: 'a',
            self.fields[1].ident: 'b',
            self.fields[2].ident: 'c',
        }
        self.assertEqual(expected, submission.as_dict())

    def test_field_names(self):
        self.submit('a', 'b', 'c')
        field_names = FormSubmission.objects.field_names()
        field_names.pop('created_at')
        self.assertEqual(field_names, {
            self.fields[0].ident: 'field 1',
            self.fields[1].ident: 'field 2',
            self.fields[2].ident: 'field 3',
        })

        self.fields[0].label = 'field 1 edited'
        self.fields[0].save()

        self.submit('a', 'b', 'c')

        field_names = FormSubmission.objects.field_names()
        field_names.pop('created_at')
        self.assertEqual(field_names, {
            self.fields[0].ident: 'field 1 edited',
            self.fields[1].ident: 'field 2',
            self.fields[2].ident: 'field 3',
        })

    def test_prefetch_submission_count(self):
        forms = list(Form.objects.all().annotate_submission_count())
        self.assertEqual(forms[0].submission_count, 0)

        new_form, new_fields = self.make_form()
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c', form=new_form)

        forms = list(Form.objects.all().annotate_submission_count())
        self.assertEqual(forms[0].submission_count, 2)
        self.assertEqual(forms[1].submission_count, 1)

        for form in forms:
            self.assertEqual(len(form.submissions), form.submission_count)

    def test_submission_count(self):
        new_form, new_fields = self.make_form()
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c', form=new_form)

        forms = list(Form.objects.all())
        for form in forms:
            self.assertEqual(len(form.submissions), form.submission_count)

    def test_name_is_preserved_after_field_is_deleted(self):
        self.submit('a', 'b', 'c')
        ident = self.fields[0].ident
        self.fields[0].delete()

        field_names = FormSubmission.objects.field_names()
        field_names.pop('created_at')
        self.assertEqual(field_names, {
            ident: 'field 1',
            self.fields[1].ident: 'field 2',
            self.fields[2].ident: 'field 3',
        })

    def test_as_dictionaries(self):
        letters = self.submit('a', 'b', 'c')
        numbers = self.submit('1', '2', '3')

        letters_dict, numbers_dict = FormSubmission.objects.as_dictionaries()

        self.assertEqual(letters.as_dict(), letters_dict)
        self.assertEqual(numbers.as_dict(), numbers_dict)

    def test_as_ordered_dictionaries(self):
        with mock_now() as first:<commit_msg>handle mysql's lack of millisecond timestamp precision<commit_after>from __future__ import unicode_literals

import contextlib

from django.test import TestCase
from django.test.client import RequestFactory
from django import forms
from django.utils import timezone
from django.core import mail
from django.utils import unittest
from django.db import connection

import mock

from modeltests.core_tests.widgy_config import widgy_site
from widgy.contrib.form_builder.models import (
    Form, FormInput, Textarea, FormSubmission, FormField, Uncaptcha,
    EmailUserHandler, EmailSuccessHandler
)
from widgy.exceptions import ParentChildRejection
from widgy.utils import build_url
from widgy.models import VersionTracker


class GetFormTest(TestCase):
    def setUp(self):
        self.form = Form.add_root(widgy_site)

    def test_get_form(self):
        input1 = self.form.children['fields'].add_child(widgy_site, FormInput)
        input1.type = 'text'
        input1.label = 'Test'
        input1.save()

        input2 = self.form.children['fields'].add_child(widgy_site, FormInput)
        input2.type = 'text'
        input2.label = 'Test 2'
        input2.save()

        form_class = self.form.build_form_class()

        data = {
            input1.get_formfield_name(): 'foo',
            input2.get_formfield_name(): 'bar',
        }

        f = form_class(data)
        self.assertTrue(f.is_valid())
        self.assertEqual(f.cleaned_data, data)

    def test_get_formfield(self):
        self.form = Form.add_root(widgy_site)

        input = self.form.children['fields'].add_child(widgy_site, FormInput)
        input.type = 'text'
        input.label = 'Test'
        input.save()

        field = input.get_formfield()

        self.assertTrue(isinstance(field, forms.CharField))
        self.assertEqual(field.label, 'Test')

    def test_with_uncaptcha(self):
        uncaptcha = self.form.children['fields'].add_child(widgy_site, Uncaptcha)
        form_class = self.form.build_form_class()
        self.assertTrue(hasattr(form_class, 'clean_%s' % uncaptcha.get_formfield_name()))


@contextlib.contextmanager
def mock_now():
    now = timezone.now()
    with mock.patch('django.utils.timezone.now') as tz_now:
        tz_now.return_value = now
        yield now


class TestForm(TestCase):
    def make_form(self):
        form = Form.add_root(widgy_site)
        fields = []
        fields.append(form.children['fields'].add_child(widgy_site, FormInput,
                                                        label='field 1',
                                                        type='text'))
        fields.append(form.children['fields'].add_child(widgy_site, FormInput,
                                                        label='field 2',
                                                        type='text'))
        fields.append(form.children['fields'].add_child(widgy_site, Textarea,
                                                        label='field 3',))
        return form, fields

    def setUp(self):
        self.form, self.fields = self.make_form()

    def submit(self, a, b, c, form=None):
        form = form or self.form
        fields = [i for i in form.children['fields'].get_children() if isinstance(i, FormField)]

        return FormSubmission.objects.submit(
            form=form or self.form,
            data={
                fields[0].get_formfield_name(): a,
                fields[1].get_formfield_name(): b,
                fields[2].get_formfield_name(): c,
            })

    def test_delete_doesnt_delete(self):
        form = Form.add_root(widgy_site)
        input = form.children['fields'].add_child(widgy_site, FormInput)

        form.delete()

        form = Form.objects.get(pk=form.pk)
        self.assertTrue(form.node.is_root())
        self.assertIn(input, form.children['fields'].get_children())

    def test_as_dict(self):
        with mock_now() as now:
            submission = self.submit('a', 'b', 'c')
        expected = {
            'created_at': now,
            self.fields[0].ident: 'a',
            self.fields[1].ident: 'b',
            self.fields[2].ident: 'c',
        }
        self.assertEqual(expected, submission.as_dict())

    def test_field_names(self):
        self.submit('a', 'b', 'c')
        field_names = FormSubmission.objects.field_names()
        field_names.pop('created_at')
        self.assertEqual(field_names, {
            self.fields[0].ident: 'field 1',
            self.fields[1].ident: 'field 2',
            self.fields[2].ident: 'field 3',
        })

        self.fields[0].label = 'field 1 edited'
        self.fields[0].save()

        self.submit('a', 'b', 'c')

        field_names = FormSubmission.objects.field_names()
        field_names.pop('created_at')
        self.assertEqual(field_names, {
            self.fields[0].ident: 'field 1 edited',
            self.fields[1].ident: 'field 2',
            self.fields[2].ident: 'field 3',
        })

    def test_prefetch_submission_count(self):
        forms = list(Form.objects.all().annotate_submission_count())
        self.assertEqual(forms[0].submission_count, 0)

        new_form, new_fields = self.make_form()
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c', form=new_form)

        forms = list(Form.objects.all().annotate_submission_count())
        self.assertEqual(forms[0].submission_count, 2)
        self.assertEqual(forms[1].submission_count, 1)

        for form in forms:
            self.assertEqual(len(form.submissions), form.submission_count)

    def test_submission_count(self):
        new_form, new_fields = self.make_form()
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c')
        self.submit('a', 'b', 'c', form=new_form)

        forms = list(Form.objects.all())
        for form in forms:
            self.assertEqual(len(form.submissions), form.submission_count)

    def test_name_is_preserved_after_field_is_deleted(self):
        self.submit('a', 'b', 'c')
        ident = self.fields[0].ident
        self.fields[0].delete()

        field_names = FormSubmission.objects.field_names()
        field_names.pop('created_at')
        self.assertEqual(field_names, {
            ident: 'field 1',
            self.fields[1].ident: 'field 2',
            self.fields[2].ident: 'field 3',
        })

    def test_as_dictionaries(self):
        letters = self.submit('a', 'b', 'c')
        numbers = self.submit('1', '2', '3')

        letters_dict, numbers_dict = FormSubmission.objects.as_dictionaries()

        # Mysql doesn't have millisecond precision for DateTimes, so refetch
        # these objects to portably do it.
        letters = FormSubmission.objects.get(pk=letters.pk)
        numbers = FormSubmission.objects.get(pk=numbers.pk)

        self.assertEqual(letters.as_dict(), letters_dict)
        self.assertEqual(numbers.as_dict(), numbers_dict)

    @unittest.skipIf(connection.vendor == 'mysql',
                     "must have millisecond DateTime precision to order form submissions")
    def test_as_ordered_dictionaries(self):
        with mock_now() as first:
{'fraction_non_alphanumeric': 0.12360521200777481, 'fraction_numerical': 0.007342883881649989, 'mean_word_length': 2.3474698795180724, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4759453', 'n_tokens_mistral': 4211, 'n_tokens_neox': 4084, 'n_words': 777}