diff --git a/.gitignore b/.gitignore
index cc9186b3..c08e5b70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ ghostdriver.log
/.cache/
/htmlcov/
*.py,cover
+.idea/
MANIFEST.in~
MIND_BUCKET.rst
diff --git a/.hgignore b/.hgignore
index 19c0346f..82425ef6 100644
--- a/.hgignore
+++ b/.hgignore
@@ -10,6 +10,7 @@ syntax: regexp
\.travis\.yml~
^htmlcov/
\.py,cover
+\.idea/
^MANIFEST\.in~
^tmp/
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 3114542b..e1d97aed 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -15,6 +15,19 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.
+0.10.6
+------
+2017-02-14
+
+- Minor Python 3 fixes for integer, float and decimal fields.
+
+0.10.5
+------
+2017-02-13
+
+- Tested against Python 3.6.
+- Initial (experimental) Django 1.11 support.
+
0.10.4
------
2017-01-11
diff --git a/README.rst b/README.rst
index 439ac37c..5f3f0b38 100644
--- a/README.rst
+++ b/README.rst
@@ -12,8 +12,15 @@ Prerequisites
=============
Present
-------
-- Django 1.8, 1.9, 1.10
-- Python >= 2.7, >= 3.4, PyPy
+- Django 1.8, 1.9, 1.10 and 1.11.
+- Python 2.7, 3.4, 3.5, 3.6 and PyPy.
+
+Note, that Django 1.11 is not yet proclaimed to be flawlessly supported. The
+core and contrib packages have been tested against the alpha Django 1.11a1
+PyPI release. All tests have successfully passed, although it's yet too early
+to claim that Django 1.11 is fully supported. Certain dependencies
+(``django-formtools`` and ``easy-thumbnails``) have been installed from source
+(since versions supporting Django 1.11 are not yet released on PyPI.)
Past
----
diff --git a/ROADMAP.rst b/ROADMAP.rst
index 67553cc1..a7623561 100644
--- a/ROADMAP.rst
+++ b/ROADMAP.rst
@@ -1,5 +1,8 @@
-Roadmap of upcoming releases
-============================
+=======
+Roadmap
+=======
+Upcoming releases.
+
0.11
----
yyyy-mm-dd (upcoming).
diff --git a/docs/index.rst b/docs/index.rst
index e02a9a11..1136d1e2 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -12,8 +12,15 @@ Prerequisites
=============
Present
-------
-- Django 1.8, 1.9, 1.10
-- Python >= 2.7, >= 3.4, PyPy
+- Django 1.8, 1.9, 1.10 and 1.11.
+- Python 2.7, 3.4, 3.5, 3.6 and PyPy.
+
+Note, that Django 1.11 is not yet proclaimed to be flawlessly supported. The
+core and contrib packages have been tested against the alpha Django 1.11a1
+PyPI release. All tests have successfully passed, although it's yet too early
+to claim that Django 1.11 is fully supported. Certain dependencies
+(``django-formtools`` and ``easy-thumbnails``) have been installed from source
+(since versions supporting Django 1.11 are not yet released on PyPI.)
Past
----
diff --git a/SCREENSHOTS.rst b/docs/screenshots.rst.distrib
similarity index 100%
rename from SCREENSHOTS.rst
rename to docs/screenshots.rst.distrib
diff --git a/examples/requirements/common_python2.txt b/examples/requirements/common.txt
similarity index 83%
rename from examples/requirements/common_python2.txt
rename to examples/requirements/common.txt
index 8a64c212..a0f838d0 100644
--- a/examples/requirements/common_python2.txt
+++ b/examples/requirements/common.txt
@@ -1,14 +1,11 @@
--r common_test_requirements.txt
--r style_checkers.txt
+#-r test.txt
+#-r style_checkers.txt
alabaster==0.7.6
Babel==2.1.1
decorator==4.0.4
docopt==0.4.0
docutils==0.12
-ipdb==0.8.1
-ipython==4.0.0
-ipython-genutils==0.1.0
Jinja2==2.8
mailchimp==2.0.9
MarkupSafe==0.23
diff --git a/examples/requirements/common_python3.txt b/examples/requirements/common_python3.txt
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/requirements/debug.txt b/examples/requirements/debug.txt
new file mode 100644
index 00000000..d811eb9d
--- /dev/null
+++ b/examples/requirements/debug.txt
@@ -0,0 +1,3 @@
+ipdb==0.8.1
+ipython==4.0.0
+ipython-genutils==0.1.0
diff --git a/examples/requirements/demo.txt b/examples/requirements/demo.txt
index e173f48a..1a9e7371 100644
--- a/examples/requirements/demo.txt
+++ b/examples/requirements/demo.txt
@@ -1,11 +1,10 @@
--r common_python2.txt
+-r common.txt
Django>=1.9,<1.10
django-admin-tools>=0.7.1
django-autoslug==1.9.3
django-debug-toolbar==0.11
django-formtools==1.0
-#django-localeurl==2.0.2
django-nine>=0.1.10
django-nonefield>=0.1
django-registration-redux>=1.4
diff --git a/examples/requirements/django_1_10.txt b/examples/requirements/django_1_10.txt
index cb2d975c..0dcf0de8 100644
--- a/examples/requirements/django_1_10.txt
+++ b/examples/requirements/django_1_10.txt
@@ -1,13 +1,9 @@
--r common_python2.txt
+-r common.txt
+-r test.txt
+-r style_checkers.txt
Django>=1.10,<1.11
django-admin-tools>=0.8.0
-#django-autoslug==1.9.3
django-debug-toolbar==1.5
-#django-formtools==1.0
-#django-nine>=0.1.10
-#django-nonefield==0.1
django-registration-redux>=1.4
-#easy-thumbnails==2.3
-#vishap>=0.1.5
sqlparse==0.2.2
diff --git a/examples/requirements/django_1_11.txt b/examples/requirements/django_1_11.txt
new file mode 100644
index 00000000..960cf11a
--- /dev/null
+++ b/examples/requirements/django_1_11.txt
@@ -0,0 +1,15 @@
+-r common.txt
+-r test.txt
+-r style_checkers.txt
+
+Django==1.11b1
+django-admin-tools>=0.8.0
+django-debug-toolbar==1.5
+django-registration-redux>=1.4
+sqlparse==0.2.2
+
+# easy-thumbnails, compatible with Django 1.11
+https://github.com/django/django-formtools/archive/master.tar.gz
+
+# django-formtools, compatible with Django 1.11
+https://github.com/SmileyChris/easy-thumbnails/archive/master.tar.gz
diff --git a/examples/requirements/django_1_5.txt b/examples/requirements/django_1_5.txt
index d671e119..ccfa5147 100644
--- a/examples/requirements/django_1_5.txt
+++ b/examples/requirements/django_1_5.txt
@@ -1,14 +1,11 @@
--r common_python2.txt
+-r common.txt
+-r test.txt
+-r style_checkers.txt
Django>=1.5,<1.6
South>=0.8.2
django-admin-tools==0.5.2
-#django-autoslug>=1.7.1
django-debug-toolbar>=0.11.0
-django-localeurl>=2.0.2
-#django-nine>=0.1.10
-#django-nonefield>=0.1
+#django-localeurl>=2.0.2
django-registration-redux==1.2
-#easy-thumbnails==2.3
-#vishap>=0.1.5
sqlparse==0.1.9
diff --git a/examples/requirements/django_1_6.txt b/examples/requirements/django_1_6.txt
index 9213495c..24bdabc9 100644
--- a/examples/requirements/django_1_6.txt
+++ b/examples/requirements/django_1_6.txt
@@ -1,14 +1,11 @@
--r common_python2.txt
+-r common.txt
+-r test.txt
+-r style_checkers.txt
Django>=1.6,<1.7
South>=0.8.2
django-admin-tools==0.5.2
-#django-autoslug>=1.7.1
django-debug-toolbar>=0.11.0
-django-localeurl>=2.0.2
-#django-nine>=0.1.10
-#django-nonefield>=0.1
+#django-localeurl>=2.0.2
django-registration-redux==1.2
-#easy-thumbnails==2.3
-#vishap>=0.1.5
sqlparse==0.1.9
diff --git a/examples/requirements/django_1_7.txt b/examples/requirements/django_1_7.txt
index 1d7dc808..0305cc16 100644
--- a/examples/requirements/django_1_7.txt
+++ b/examples/requirements/django_1_7.txt
@@ -1,13 +1,10 @@
--r common_python2.txt
+-r common.txt
+-r test.txt
+-r style_checkers.txt
Django>=1.7,<1.8
django-admin-tools>=0.6.0
-#django-autoslug==1.7.1
django-debug-toolbar==0.11.0
-django-localeurl>=2.0.2
-#django-nine>=0.1.10
-#django-nonefield>=0.1
+#django-localeurl>=2.0.2
django-registration-redux==1.3
-#easy-thumbnails==2.3
-#vishap>=0.1.5
sqlparse==0.1.9
\ No newline at end of file
diff --git a/examples/requirements/django_1_8.txt b/examples/requirements/django_1_8.txt
index 47223f35..c6668b54 100644
--- a/examples/requirements/django_1_8.txt
+++ b/examples/requirements/django_1_8.txt
@@ -1,14 +1,10 @@
--r common_python2.txt
+-r common.txt
+-r test.txt
+-r style_checkers.txt
Django>=1.8,<1.9
django-admin-tools>=0.6.0
-#django-autoslug==1.7.1
django-debug-toolbar==0.11.0
django-formtools
-#django-localeurl>=2.0.2
-#django-nine>=0.1.10
-#django-nonefield>=0.1
django-registration-redux>=1.4
-#easy-thumbnails==2.3
-#vishap>=0.1.5
-sqlparse==0.1.9
\ No newline at end of file
+sqlparse==0.1.9
diff --git a/examples/requirements/django_1_9.txt b/examples/requirements/django_1_9.txt
index 0f7a1757..7dff1ec6 100644
--- a/examples/requirements/django_1_9.txt
+++ b/examples/requirements/django_1_9.txt
@@ -1,4 +1,6 @@
--r common_python2.txt
+-r common.txt
+-r test.txt
+-r style_checkers.txt
Django>=1.9,<1.10
django-admin-tools>=0.7.1
diff --git a/examples/requirements/docs.txt b/examples/requirements/docs.txt
index 7872c874..ba95855d 100644
--- a/examples/requirements/docs.txt
+++ b/examples/requirements/docs.txt
@@ -1,21 +1,6 @@
--r common_python2.txt
+-r common.txt
+-r django_1_10.txt
-Django==1.10.1
django-fobi
-#Jinja2
-#MarkupSafe
-#MySQL-python
-#mailchimp
-#Sphinx
-django-admin-tools>=0.8.0
-django-debug-toolbar>=1.5
-django-registration-redux>=1.4
-#docutils
-#ipdb
-#ipython
-#ordereddict>=1.1
-# Selenium shall always be upgraded
-#selenium
-#tox
rst2pdf
reportlab==3.3.0
diff --git a/examples/requirements/latest.txt b/examples/requirements/latest.txt
index 7bd0d8de..fe7439a7 100644
--- a/examples/requirements/latest.txt
+++ b/examples/requirements/latest.txt
@@ -1,4 +1,4 @@
--r common_python2.txt
+-r common.txt
Django
django-admin-tools>=0.7.1
diff --git a/examples/requirements/python_3.txt b/examples/requirements/python_3.txt
deleted file mode 100644
index e69de29b..00000000
diff --git a/examples/requirements/common_test_requirements.txt b/examples/requirements/test.txt
similarity index 100%
rename from examples/requirements/common_test_requirements.txt
rename to examples/requirements/test.txt
diff --git a/examples/requirements/testing.txt b/examples/requirements/testing.txt
new file mode 100644
index 00000000..a51e40ab
--- /dev/null
+++ b/examples/requirements/testing.txt
@@ -0,0 +1,3 @@
+-r django_1_10.txt
+-r test.txt
+-r style_checkers.txt
diff --git a/examples/simple/bar/forms.py b/examples/simple/bar/forms.py
new file mode 100644
index 00000000..456efd2b
--- /dev/null
+++ b/examples/simple/bar/forms.py
@@ -0,0 +1,6 @@
+from django import forms
+
+class MyForm(forms.Form):
+ """Test form."""
+
+ number = forms.IntegerField(max_value=200)
diff --git a/examples/simple/bar/templates/bar/form.html b/examples/simple/bar/templates/bar/form.html
new file mode 100644
index 00000000..ab70a5ce
--- /dev/null
+++ b/examples/simple/bar/templates/bar/form.html
@@ -0,0 +1,17 @@
+
+
+
+
+ Title
+
+
+
+
+
+
+
diff --git a/examples/simple/bar/urls.py b/examples/simple/bar/urls.py
new file mode 100644
index 00000000..34a6f602
--- /dev/null
+++ b/examples/simple/bar/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import include, url
+from django.utils.translation import ugettext_lazy as _
+
+from .views import my_view
+
+urlpatterns = [
+ url(_(r'^$'), my_view, name='bar.my_view'),
+]
diff --git a/examples/simple/bar/views.py b/examples/simple/bar/views.py
new file mode 100644
index 00000000..7e44ff68
--- /dev/null
+++ b/examples/simple/bar/views.py
@@ -0,0 +1,13 @@
+from django.shortcuts import render
+
+from .forms import MyForm
+
+def my_view(request):
+ if request.method == 'POST':
+ form = MyForm(data=request.POST)
+ else:
+ form = MyForm()
+
+ context = {'form': form}
+
+ return render(request, 'bar/form.html', context)
\ No newline at end of file
diff --git a/examples/simple/runserver/bootstrap3-theme-django-1-11.sh b/examples/simple/runserver/bootstrap3-theme-django-1-11.sh
new file mode 100755
index 00000000..b2ef79e6
--- /dev/null
+++ b/examples/simple/runserver/bootstrap3-theme-django-1-11.sh
@@ -0,0 +1 @@
+./manage.py runserver 0.0.0.0:8000 --traceback -v 3 --settings=settings.bootstrap3_theme_django_1_11 --traceback -v 3
diff --git a/examples/simple/settings/bootstrap3_theme_django_1_11.py b/examples/simple/settings/bootstrap3_theme_django_1_11.py
new file mode 100644
index 00000000..4a5d0b53
--- /dev/null
+++ b/examples/simple/settings/bootstrap3_theme_django_1_11.py
@@ -0,0 +1,19 @@
+from .base import *
+
+INSTALLED_APPS = list(INSTALLED_APPS)
+
+try:
+ INSTALLED_APPS.remove('south') if 'south' in INSTALLED_APPS else None
+ INSTALLED_APPS.remove('tinymce') if 'tinymce' in INSTALLED_APPS else None
+except Exception as err:
+ pass
+
+try:
+ INSTALLED_APPS.remove('admin_tools') \
+ if 'admin_tools' in INSTALLED_APPS else None
+ INSTALLED_APPS.remove('admin_tools.menu') \
+ if 'admin_tools.menu' in INSTALLED_APPS else None
+ INSTALLED_APPS.remove('admin_tools.dashboard') \
+ if 'admin_tools.dashboard' in INSTALLED_APPS else None
+except Exception as err:
+ pass
diff --git a/examples/simple/urls.py b/examples/simple/urls.py
index b723034e..52ea6504 100644
--- a/examples/simple/urls.py
+++ b/examples/simple/urls.py
@@ -57,6 +57,9 @@ url_patterns_args = [
# foo URLs:
url(r'^foo/', include('foo.urls')),
+ # bar URLs:
+ # url(r'^bar/', include('bar.urls')),
+
url(r'^$', TemplateView.as_view(template_name=fobi_home_template)),
# django-fobi public forms contrib app:
diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh
index 922a43fc..020b17da 100755
--- a/scripts/build_docs.sh
+++ b/scripts/build_docs.sh
@@ -1,6 +1,6 @@
#./scripts/uninstall.sh
#./scripts/install.sh
-cat README.rst SCREENSHOTS.rst docs/documentation.rst.distrib > docs/index.rst
+cat README.rst docs/screenshots.rst.distrib docs/documentation.rst.distrib > docs/index.rst
cat QUICK_START.rst > docs/quickstart.rst
sphinx-build -n -a -b html docs builddocs
#sphinx-build -n -a -b pdf docs builddocs
diff --git a/scripts/clean_up.sh b/scripts/clean_up.sh
index 12943a8a..51cb5474 100755
--- a/scripts/clean_up.sh
+++ b/scripts/clean_up.sh
@@ -1,5 +1,6 @@
find . -name "*.pyc" -exec rm -rf {} \;
find . -name "*.py,cover" -exec rm -rf {} \;
+find . -name "*.orig" -exec rm -rf {} \;
find . -name "__pycache__" -exec rm -rf {} \;
rm -rf build/
rm -rf dist/
diff --git a/scripts/install_django_1_11.sh b/scripts/install_django_1_11.sh
new file mode 100755
index 00000000..acef833b
--- /dev/null
+++ b/scripts/install_django_1_11.sh
@@ -0,0 +1,8 @@
+pip uninstall south -y
+pip install -r examples/requirements/django_1_11.txt
+python setup.py install
+mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image
+mkdir -p examples/media/fobi_plugins/file
+python examples/simple/manage.py collectstatic --noinput --settings=settings.bootstrap3_theme_django_1_11 --traceback -v 3
+python examples/simple/manage.py migrate --noinput --settings=settings.bootstrap3_theme_django_1_11 --traceback -v 3
+python examples/simple/manage.py fobi_create_test_data --settings=settings.bootstrap3_theme_django_1_11 --traceback -v 3
diff --git a/scripts/prepare_docs.sh b/scripts/prepare_docs.sh
index b5491e4d..70ece38c 100755
--- a/scripts/prepare_docs.sh
+++ b/scripts/prepare_docs.sh
@@ -1,2 +1,2 @@
-cat README.rst SCREENSHOTS.rst docs/documentation.rst.distrib > docs/index.rst
+cat README.rst docs/screenshots.rst.distrib docs/documentation.rst.distrib > docs/index.rst
cat QUICK_START.rst > docs/quickstart.rst
diff --git a/setup.py b/setup.py
index a23b56ca..b9e61e51 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,15 @@ import sys
from distutils.version import LooseVersion
from setuptools import setup, find_packages
-version = '0.10.4'
+version = '0.10.6'
+
+# ***************************************************************************
+# ************************** Python version *********************************
+# ***************************************************************************
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+LTE_PY26 = PY2 and (7 > sys.version_info[1])
+PYPY = hasattr(sys, 'pypy_translation_info')
# ***************************************************************************
# ************************** Django version *********************************
@@ -69,7 +77,6 @@ except Exception as err:
try:
readme = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
- readme = readme.replace('.. code-block:: none', '.. code-block::')
screenshots = open(
os.path.join(os.path.dirname(__file__), 'SCREENSHOTS.rst')
).read()
@@ -78,10 +85,6 @@ try:
'.. figure:: https://github.com/barseghyanartur/django-fobi/raw/'
'master/docs/_static'
)
- screenshots = screenshots.replace(
- '.. code-block:: none',
- '.. code-block::'
- )
except:
readme = ''
screenshots = ''
@@ -206,6 +209,8 @@ for locale_dir in locale_dirs:
for f
in os.listdir(locale_dir)]
+dependency_links = []
+
install_requires = []
# If certain version of Django is already installed, choose version agnostic
# dependencies.
@@ -216,20 +221,21 @@ if DJANGO_INSTALLED:
# 'django-formtools>=1.0',
'django-nine>=0.1.10',
'django-nonefield>=0.1',
- 'ordereddict>=1.1',
+ # 'ordereddict>=1.1',
'Pillow>=2.0.0',
'requests>=1.0.0',
'six>=1.9',
'Unidecode>=0.04.1',
'vishap>=0.1.5,<2.0',
]
+
elif DJANGO_1_8:
install_requires = [
'django-autoslug==1.7.1',
'django-formtools>=1.0',
'django-nine>=0.1.10',
'django-nonefield>=0.1',
- 'ordereddict>=1.1',
+ # 'ordereddict>=1.1',
'Pillow>=2.0.0',
'requests>=1.0.0',
'six>=1.9',
@@ -242,7 +248,7 @@ if DJANGO_INSTALLED:
'django-formtools>=1.0',
'django-nine>=0.1.10',
'django-nonefield>=0.1',
- 'ordereddict>=1.1',
+ # 'ordereddict>=1.1',
'Pillow>=2.0.0',
'requests>=1.0.0',
'six>=1.9',
@@ -255,13 +261,31 @@ if DJANGO_INSTALLED:
'django-formtools>=1.0',
'django-nine>=0.1.10',
'django-nonefield>=0.1',
- 'ordereddict>=1.1',
+ # 'ordereddict>=1.1',
'Pillow>=2.0.0',
'requests>=1.0.0',
'six>=1.9',
'Unidecode>=0.04.1',
'vishap>=0.1.5,<2.0',
]
+ elif DJANGO_1_11:
+ install_requires = [
+ 'django-autoslug==1.9.3',
+ 'django-formtools',
+ 'django-nine>=0.1.10',
+ 'django-nonefield>=0.1',
+ # 'ordereddict>=1.1',
+ 'Pillow>=2.0.0',
+ 'requests>=1.0.0',
+ 'six>=1.9',
+ 'Unidecode>=0.04.1',
+ 'vishap>=0.1.5,<2.0',
+ ]
+ dependency_links.append(
+ 'https://github.com/django/django-formtools/archive/master.tar.gz'
+ '#egg=django-formtools'
+ )
+
# Fall back to the latest dependencies
if not install_requires:
install_requires = [
@@ -269,7 +293,7 @@ if not install_requires:
'django-formtools>=1.0',
'django-nine>=0.1.10',
'django-nonefield>=0.1',
- 'ordereddict>=1.1',
+ # 'ordereddict>=1.1',
'Pillow>=2.0.0',
'requests>=1.0.0',
'six>=1.9',
@@ -290,24 +314,35 @@ tests_require = [
# 'tox',
]
-try:
- PY2 = sys.version_info[0] == 2
- LTE_PY26 = PY2 and (7 > sys.version_info[1])
- PY3 = sys.version_info[0] == 3
- if PY3:
- install_requires.append('simplejson>=3.0.0') # When using Python 3
+if PY3:
+ install_requires.append('simplejson>=3.0.0') # When using Python 3
+ if not DJANGO_1_11:
install_requires.append('easy-thumbnails>=2.3')
else:
- install_requires.append('simplejson>=2.1.0') # When using Python 2.*
+ install_requires.append('easy-thumbnails')
+ dependency_links.append(
+ 'https://github.com/SmileyChris/easy-thumbnails/archive/'
+ 'master.tar.gz'
+ '#egg=easy-thumbnails'
+ )
+else:
+ install_requires.append('simplejson>=2.1.0') # When using Python 2.*
+ install_requires.append('ordereddict>=1.1')
+ if not DJANGO_1_11:
install_requires.append('easy-thumbnails>=1.4')
-except Exception as err:
- pass
+ else:
+ install_requires.append('easy-thumbnails')
+ dependency_links.append(
+ 'https://github.com/SmileyChris/easy-thumbnails/archive/'
+ 'master.tar.gz'
+ '#egg=easy-thumbnails'
+ )
setup(
name='django-fobi',
version=version,
- description=("Form generator/builder application for Django done right: "
- "customisable, modular, user- and developer- friendly."),
+ description="Form generator/builder application for Django done right: "
+ "customisable, modular, user- and developer- friendly.",
long_description="{0}{1}".format(readme, screenshots),
classifiers=[
# "Programming Language :: Python :: 2.6",
@@ -316,6 +351,7 @@ setup(
# "Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
"Environment :: Web Environment",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"License :: OSI Approved :: GNU Lesser General Public License v2 or "
@@ -335,6 +371,7 @@ setup(
license='GPL 2.0/LGPL 2.1',
install_requires=install_requires,
tests_require=tests_require,
+ dependency_links=dependency_links,
package_data={
'fobi': templates + static_files + locale_files
},
diff --git a/src/fobi/__init__.py b/src/fobi/__init__.py
index 790fb4ce..69733cf5 100644
--- a/src/fobi/__init__.py
+++ b/src/fobi/__init__.py
@@ -1,6 +1,6 @@
__title__ = 'django-fobi'
-__version__ = '0.10.4'
-__build__ = 0x000077
+__version__ = '0.10.6'
+__build__ = 0x000079
__author__ = 'Artur Barseghyan '
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
diff --git a/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py b/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py
index 1a9e2278..42ace84b 100644
--- a/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py
+++ b/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py
@@ -1,5 +1,7 @@
from __future__ import absolute_import
+import decimal
+
from django.forms.fields import DecimalField
from django.utils.translation import ugettext_lazy as _
@@ -41,19 +43,24 @@ class DecimalInputPlugin(FormFieldPlugin):
'initial': self.data.initial,
'required': self.data.required,
}
+
if self.data.max_value:
- field_kwargs['max_value'] = self.data.max_value
- widget_attrs['max'] = self.data.max_value
+ data_max_value = decimal.Decimal(self.data.max_value)
+ field_kwargs['max_value'] = data_max_value
+ widget_attrs['max'] = data_max_value
+
if self.data.min_value:
- field_kwargs['min_value'] = self.data.min_value
- widget_attrs['min'] = self.data.min_value
+ data_min_value = decimal.Decimal(self.data.min_value)
+ field_kwargs['min_value'] = data_min_value
+ widget_attrs['min'] = data_min_value
if self.data.max_digits:
- field_kwargs['max_digits'] = self.data.max_digits
- widget_attrs['max'] = self.data.max_value
+ data_max_digits = int(self.data.max_digits)
+ field_kwargs['max_digits'] = data_max_digits
+
if self.data.decimal_places:
- field_kwargs['decimal_places'] = self.data.decimal_places
- widget_attrs['min'] = self.data.min_value
+ data_decimal_places = int(self.data.decimal_places)
+ field_kwargs['decimal_places'] = data_decimal_places
field_kwargs['widget'] = NumberInput(attrs=widget_attrs)
diff --git a/src/fobi/contrib/plugins/form_elements/fields/float/base.py b/src/fobi/contrib/plugins/form_elements/fields/float/base.py
index 59182a25..c7909c40 100644
--- a/src/fobi/contrib/plugins/form_elements/fields/float/base.py
+++ b/src/fobi/contrib/plugins/form_elements/fields/float/base.py
@@ -34,18 +34,23 @@ class FloatInputPlugin(FormFieldPlugin):
'type': 'number',
'placeholder': self.data.placeholder,
}
+
field_kwargs = {
'label': self.data.label,
'help_text': self.data.help_text,
'initial': self.data.initial,
'required': self.data.required,
}
+
if self.data.max_value:
- field_kwargs['max_value'] = self.data.max_value
- widget_attrs['max'] = self.data.max_value
+ data_max_value = float(data.max_value)
+ field_kwargs['max_value'] = data_max_value
+ widget_attrs['max'] = data_max_value
+
if self.data.min_value:
- field_kwargs['min_value'] = self.data.min_value
- widget_attrs['min'] = self.data.min_value
+ data_min_value = float(self.data.min_value)
+ field_kwargs['min_value'] = data_min_value
+ widget_attrs['min'] = data_min_value
field_kwargs['widget'] = NumberInput(attrs=widget_attrs)
diff --git a/src/fobi/contrib/plugins/form_elements/fields/integer/base.py b/src/fobi/contrib/plugins/form_elements/fields/integer/base.py
index 0a73c7a1..859c0544 100644
--- a/src/fobi/contrib/plugins/form_elements/fields/integer/base.py
+++ b/src/fobi/contrib/plugins/form_elements/fields/integer/base.py
@@ -41,11 +41,13 @@ class IntegerInputPlugin(FormFieldPlugin):
'required': self.data.required,
}
if self.data.max_value:
- field_kwargs['max_value'] = self.data.max_value
- widget_attrs['max'] = self.data.max_value
+ data_max_value = int(self.data.max_value)
+ field_kwargs['max_value'] = data_max_value
+ widget_attrs['max'] = data_max_value
if self.data.min_value:
- field_kwargs['min_value'] = self.data.min_value
- widget_attrs['min'] = self.data.min_value
+ data_min_value = int(self.data.min_value)
+ field_kwargs['min_value'] = data_min_value
+ widget_attrs['min'] = data_min_value
field_kwargs['widget'] = NumberInput(attrs=widget_attrs)
diff --git a/src/fobi/tests/helpers.py b/src/fobi/tests/helpers.py
index 67b4992f..f966393d 100644
--- a/src/fobi/tests/helpers.py
+++ b/src/fobi/tests/helpers.py
@@ -1,3 +1,7 @@
+import os
+import signal
+import subprocess
+
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
@@ -46,6 +50,7 @@ __all__ = (
'get_or_create_admin_user',
'create_form_with_entries',
'db_clean_up',
+ 'phantom_js_clean_up',
)
# ****************************************************************************
@@ -272,3 +277,17 @@ def db_clean_up():
"""
FormElementEntry._default_manager.all().delete()
FormHandlerEntry._default_manager.all().delete()
+
+
+def phantom_js_clean_up():
+ """Clean up Phantom JS.
+
+ Kills all phantomjs instances, disregard of their origin.
+ """
+ processes = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
+ out, err = processes.communicate()
+
+ for line in out.splitlines():
+ if 'phantomjs' in line:
+ pid = int(line.split(None, 1)[0])
+ os.kill(pid, signal.SIGKILL)
diff --git a/src/fobi/tests/test_browser_build_dynamic_forms.py b/src/fobi/tests/test_browser_build_dynamic_forms.py
index 5383d601..57fde797 100644
--- a/src/fobi/tests/test_browser_build_dynamic_forms.py
+++ b/src/fobi/tests/test_browser_build_dynamic_forms.py
@@ -27,7 +27,8 @@ from .data import (
from .helpers import (
setup_fobi,
get_or_create_admin_user,
- db_clean_up
+ db_clean_up,
+ phantom_js_clean_up
)
@@ -75,23 +76,23 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
@classmethod
def setUpClass(cls):
"""Set up class."""
- # cls.selenium = WebDriver()
+ # cls.driver = WebDriver()
firefox_bin_path = getattr(settings, 'FIREFOX_BIN_PATH', None)
phantom_js_executable_path = getattr(
settings, 'PHANTOM_JS_EXECUTABLE_PATH', None
)
if phantom_js_executable_path is not None:
if phantom_js_executable_path:
- cls.selenium = webdriver.PhantomJS(
+ cls.driver = webdriver.PhantomJS(
executable_path=phantom_js_executable_path
)
else:
- cls.selenium = webdriver.PhantomJS()
+ cls.driver = webdriver.PhantomJS()
elif firefox_bin_path:
binary = FirefoxBinary(firefox_bin_path)
- cls.selenium = webdriver.Firefox(firefox_binary=binary)
+ cls.driver = webdriver.Firefox(firefox_binary=binary)
else:
- cls.selenium = webdriver.Firefox()
+ cls.driver = webdriver.Firefox()
setup_fobi(fobi_sync_plugins=True)
# user = get_or_create_admin_user()
@@ -103,7 +104,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
def tearDownClass(cls):
"""Tear down class."""
try:
- cls.selenium.quit()
+ cls.driver.quit()
+ phantom_js_clean_up()
except Exception as err:
print(err)
@@ -137,21 +139,21 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# Make sure the user exists
user = get_or_create_admin_user()
- self.selenium.get(
+ self.driver.get(
'{0}{1}'.format(
self._get_live_server_url(),
reverse('auth_login')
)
)
- self.selenium.maximize_window()
- username_input = self.selenium.find_element_by_name("username")
+ self.driver.maximize_window()
+ username_input = self.driver.find_element_by_name("username")
username_input.send_keys(constants.FOBI_TEST_USER_USERNAME)
- password_input = self.selenium.find_element_by_name("password")
+ password_input = self.driver.find_element_by_name("password")
password_input.send_keys(constants.FOBI_TEST_USER_PASSWORD)
- self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
+ self.driver.find_element_by_xpath('//button[@type="submit"]').click()
# Wait until the list view opens
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
# lambda driver: driver.find_element_by_id('id_main')
lambda driver: driver.find_element_by_xpath(
'//body[contains(@class, "theme")]'
@@ -161,11 +163,16 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
def _sleep(self, wait=WAIT_FOR):
"""Sleep."""
if WAIT and wait:
- self.selenium.implicitly_wait(wait)
+ self.driver.implicitly_wait(wait)
def _click(self, element):
"""Click on any element."""
- self.selenium.execute_script("$(arguments[0]).click();", element)
+ self.driver.execute_script("$(arguments[0]).click();", element)
+
+ def _aggressive_click(self, element):
+ """Aggressive click."""
+ link = element.get_attribute('href')
+ self.driver.get(link)
def _go_to_dashboard(self, wait=WAIT_FOR):
"""Go to dashboard."""
@@ -174,10 +181,10 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# Open the dashboard.
url = reverse('fobi.dashboard')
- self.selenium.get('{0}{1}'.format(self._get_live_server_url(), url))
+ self.driver.get('{0}{1}'.format(self._get_live_server_url(), url))
# Wait until the edit widget form opens
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
'//body[contains(@class, "theme-bootstrap3")]'
)
@@ -193,22 +200,22 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
x = coordinates.get('x', 0)
y = coordinates.get('y', 0)
- self.selenium.execute_script(
+ self.driver.execute_script(
"window.scrollTo({0}, {1});".format(x, y)
)
- self.selenium.execute_script(
+ self.driver.execute_script(
"window.scrollBy({0}, {1});".format(0, -100)
)
def _scroll_to(self, x, y):
"""Scroll to."""
- self.selenium.execute_script(
+ self.driver.execute_script(
"window.scrollTo({0}, {1});".format(x, y)
)
def _scroll_by(self, x, y):
"""Scroll by."""
- self.selenium.execute_script(
+ self.driver.execute_script(
"window.scrollBy({0}, {1});".format(x, y)
)
@@ -223,12 +230,12 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# Follow the create form link.
# Click the button to go to dashboard edit
- self.selenium.find_element_by_xpath(
+ self.driver.find_element_by_xpath(
'//a[contains(@class, "list-group-item")]'
).click()
# Wait until the dashboard edit view opens
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
# lambda driver: driver.find_element_by_id('id_main')
lambda driver: driver.find_element_by_xpath(
'//body[contains(@class, "theme-bootstrap3")]'
@@ -246,11 +253,11 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
if form_data:
for field_name, field_value in form_data.items():
- field_input = self.selenium.find_element_by_name(field_name)
+ field_input = self.driver.find_element_by_name(field_name)
field_input.send_keys(field_value)
# Click add widget button
- self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
+ self.driver.find_element_by_xpath('//button[@type="submit"]').click()
logger.debug(
"""//div[contains(text(), 'Form {0} was created """
@@ -260,7 +267,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
)
# Wait until the fobi page opens with the form element in
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//div[contains(text(), 'Form {0} was created """
"""successfully.') """
@@ -288,7 +295,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
"""
# Click the add form element button to add a new form element to the
# form.
- add_form_element_link = self.selenium.find_element_by_xpath(
+ add_form_element_link = self.driver.find_element_by_xpath(
"""//a[contains(text(), 'Choose form element to add') and """
"""contains(@class, "dropdown-toggle")]"""
)
@@ -320,7 +327,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# Adding form data
if form_element_data:
# Wait until the add widget view opens
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//h1[contains(text(), 'Add "{0}" element to """
"""the form')]""".format(
@@ -330,18 +337,18 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
)
for field_name, field_value in form_element_data.items():
- field_input = self.selenium.find_element_by_name(field_name)
+ field_input = self.driver.find_element_by_name(field_name)
field_input.send_keys(field_value)
# Click add widget button
- self.selenium.find_element_by_xpath(
+ self.driver.find_element_by_xpath(
'//button[@type="submit"]'
).click()
logger.debug(form_element_name)
# Wait until the fobi page opens with the form element in.
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//div[contains(text(), 'The form element plugin "{0}" """
"""was added successfully.') """
@@ -385,7 +392,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
"""
# Get the label of the given form element in order to delete it later
# from the form.
- delete_form_element_label = self.selenium.find_element_by_xpath(
+ delete_form_element_label = self.driver.find_element_by_xpath(
"""//label[contains(text(), '({0})') """
"""and contains(@class, "control-label")]""".format(
form_element_name
@@ -403,13 +410,14 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
.find_element_by_partial_link_text(
'Delete'
)
- delete_form_element_link.click()
+ # delete_form_element_link.click()
# self._click(delete_form_element_link)
+ self._aggressive_click(delete_form_element_link)
logger.debug(form_element_name)
# Wait until the fobi page opens with the form element in.
- WebDriverWait(self.selenium, timeout=LONG_TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=LONG_TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//div[contains(text(), 'The form element plugin "{0}" """
"""was deleted successfully.') """
@@ -442,14 +450,14 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# tab with form handlers. Otherwise Selenium raises
# an exception about non-visible element on the page
# that we're trying to fetch.
- form_handlers_tab_link = self.selenium.find_element_by_xpath(
+ form_handlers_tab_link = self.driver.find_element_by_xpath(
"""//a[@href="#tab-form-handlers"]"""
)
form_handlers_tab_link.click()
# Click the add form element button to add a new form element to the
# form.
- add_form_handler_link = self.selenium.find_element_by_xpath(
+ add_form_handler_link = self.driver.find_element_by_xpath(
"""//a[contains(text(), 'Choose form handler to add') """
"""and contains(@class, "dropdown-toggle")]"""
)
@@ -476,7 +484,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# If has config, there's a need to perform some extra tests.
if form_handler_data:
# Wait until the add widget view opens
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//h1[contains(text(), 'Add "{0}" handler to """
"""the form')]""".format(
@@ -487,16 +495,16 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# Config
for field_name, field_value in form_handler_data.items():
- field_input = self.selenium.find_element_by_name(field_name)
+ field_input = self.driver.find_element_by_name(field_name)
field_input.send_keys(field_value)
# Click add widget button
- self.selenium.find_element_by_xpath(
+ self.driver.find_element_by_xpath(
'//button[@type="submit"]'
).click()
# Wait until the fobi page opens with the form element in.
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//div[contains(text(), 'The form handler plugin "{0}" """
"""was added successfully.') """
@@ -542,14 +550,14 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# tab with form handlers. Otherwise Selenium raises
# an exception about non-visible element on the page
# that we're trying to fetch.
- form_handlers_tab_link = self.selenium.find_element_by_xpath(
+ form_handlers_tab_link = self.driver.find_element_by_xpath(
"""//a[@href="#tab-form-handlers"]"""
)
form_handlers_tab_link.click()
# Get the label of the given form element in order to delete it later
# from the form.
- delete_form_handler_label = self.selenium.find_element_by_xpath(
+ delete_form_handler_label = self.driver.find_element_by_xpath(
"""//td[contains(text(), '{0}')]""".format(form_handler_name)
)
@@ -569,7 +577,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
logger.debug(form_handler_name)
# Wait until the fobi page opens with the form element in.
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//div[contains(text(), 'The form handler plugin "{0}" """
"""was deleted successfully.') """
@@ -630,7 +638,7 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
self._test_add_form(wait=WAIT_FOR)
# Make sure the success message is there
- # self.selenium.find_element_by_xpath(
+ # self.driver.find_element_by_xpath(
# """//div[text()='Form {0} was created successfully.']""".format(
# constants.TEST_FORM_NAME
# )
@@ -665,26 +673,26 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase):
# Getting the form URL
url = reverse('fobi.view_form_entry', args=[form.slug])
- self.selenium.get('{0}{1}'.format(self._get_live_server_url(), url))
+ self.driver.get('{0}{1}'.format(self._get_live_server_url(), url))
# Wait until the edit widget form opens
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
'//body[contains(@class, "theme-bootstrap3")]'
)
)
for field_name, field_value in TEST_FORM_FIELD_DATA.items():
- field_input = self.selenium.find_element_by_name(field_name)
+ field_input = self.driver.find_element_by_name(field_name)
field_input.send_keys(field_value)
self._sleep(2)
# Click add widget button
- self.selenium.find_element_by_xpath('//button[@type="submit"]').click()
+ self.driver.find_element_by_xpath('//button[@type="submit"]').click()
# Wait until the submit success page opens a clear success message.
- WebDriverWait(self.selenium, timeout=TIMEOUT).until(
+ WebDriverWait(self.driver, timeout=TIMEOUT).until(
lambda driver: driver.find_element_by_xpath(
"""//div[contains(text(), 'Form {0} was submitted """
"""successfully.') """
diff --git a/tox.ini b/tox.ini
index 5a466a0d..d371cf8e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,8 +3,8 @@ envlist =
# py{27,33,34}-{django15,django16},
# py{27,33,34}-{django17,django18}
# py{27,34}-{django17,django18}
- py{27,34,35,py}-{django18,django19}
- py{27,35,py}-{django110}
+ py{27,34,35,36,py}-{django18,django19}
+ py{27,35,36,py}-{django110,django111}
#flake8,
#isort
@@ -18,6 +18,7 @@ deps =
django18: -r{toxinidir}/examples/requirements/django_1_8.txt
django19: -r{toxinidir}/examples/requirements/django_1_9.txt
django110: -r{toxinidir}/examples/requirements/django_1_10.txt
+ django111: -r{toxinidir}/examples/requirements/django_1_11.txt
commands =
# {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3
{envpython} runtests.py