Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
lnclt
political_memory
Commits
66980e59
Commit
66980e59
authored
Dec 12, 2015
by
Jamesie Pic
Browse files
Quality
parent
9bf87cb4
Changes
58
Hide whitespace changes
Inline
Side-by-side
.openshift/action_hooks/deploy
View file @
66980e59
...
...
@@ -7,12 +7,8 @@ set -xe
source
${
OPENSHIFT_HOMEDIR
}
app-root/runtime/dependencies/python/virtenv/bin/activate
cat
${
OPENSHIFT_REPO_DIR
}
requirements.txt
pip
install
-U
pip
pip
install
-r
${
OPENSHIFT_REPO_DIR
}
requirements.txt
# We don't have sentry yet
# python ${OPENSHIFT_REPO_DIR}manage.py raven test
...
...
.travis.yml
View file @
66980e59
sudo
:
false
env
:
matrix
:
-
DEBUG=True
-
DJANGO_DEBUG=True DJANGO_SETTINGS_MODULE=memopol.settings
language
:
python
python
:
-
'
2.7'
install
:
-
pip install
django
-
pip install
-r requirements.txt
-
pip install
-e .
-
pip install
flake8 pep8
before_script
:
-
npm install -g bower
-
bower install
-
npm install
script
:
-
./manage.py migrate
-
pep8 . --exclude '*/migrations,docs' --ignore E128
-
flake8 . --exclude '*/migrations,docs' --ignore E128
-
django-admin migrate
-
django-admin update_score
-
node_modules/gulp/bin/gulp.js less
deploy
:
-
provider
:
openshift
...
...
INSTALL
deleted
100644 → 0
View file @
9bf87cb4
How to install memopol
1. Create a directory containing the whole project (say ~memopol)
2. Create a python virtualenv in ~memopol : ~memopol/ve
3. Clone memopol main repository (http://git.laquadrature.net/memopol/memopol_political_memory) in ~memopol/memopol_political_memory
4. Clone django-representative directory (http://git.laquadrature.net/memopol/compotista_django-representatives) in ~memopol/django-representatives
5. Clone the last version of django-chronograph (https://bitbucket.org/wnielson/django-chronograph) in ~memopol/django-chronograph
(It seems that the version in pip is not up to date…)
6. Symlink ~memopol/memopol_political_memory/representatives -> ~memopol/django-representatives/representatives
7. Symlink ~memopol/memopol_political_memory/chronograph -> ~memopol/django-chronograph/chronograph
8. Install requierements in the virtualenv with pip install -r ~memopol//memopol_political_memory/requierements
9. Config your database in ~memopol/memopol_political_memory/settings.py
10. Sync the database python manage.py syncdb
11. Import mep from a compotista instance : python manage.py update update_eu_mps
12. Run the server
COPYING
→
LICENSE
View file @
66980e59
File moved
core/templates/core/blocks/pagination.haml
View file @
66980e59
-# Pagination block display pagination for the `object_list`
`object_list` could be generated with core.view_utils.render_paginate_list
.pagination-block
%nav
%ul
.pagination.pagination-sm
-
if
object_list
.
has_previous
-
if
page
.
has_previous
%li
%a
{
'href'
:
'
?
=
{
queries
.
urlencode
}
&
page
==
{
object_list
.
previous_page_number
}
',
%a
{
'href'
:
'
?
=
{
queries
.
urlencode
}
&
page
==
{
page
.
previous_page_number
}
',
'
aria
-
label
': '
Previous
'
}
<i
aria-hidden=
"true"
class=
"fa fa-chevron-left"
></i>
-
for
page_num
in
object_list
.
page_range
-
if
not
page_num
-
for
p
in
page
.
pages
-
if
p
-
if
p
==
page
.
number
%li
.active
%a
{
'href'
:
''
}
{{ p }}
-
else
%li
%a
{
'href'
:
'
?
=
{
queries
.
urlencode
}
&
page
==
{
p
}
'
}
{{ p }}
-
else
%li
.disabled
%a
{
'href'
:
''
}
…
-
elif
page_num
==
object_list
.
number
%li
.active
%a
{
'href'
:
''
}
{{ page_num }}
-
else
%li
%a
{
'href'
:
'
?
=
{
queries
.
urlencode
}
&
page
==
{
page_num
}
'
}
{{ page_num }}
-
if
object_list
.
has_next
-
if
page
.
has_next
%li
%a
{
'href'
:
'
?
=
{
queries
.
urlencode
}
&
page
==
{
object_list
.
next_page_number
}
',
%a
{
'href'
:
'
?
=
{
queries
.
urlencode
}
&
page
==
{
page
.
next_page_number
}
',
'
aria
-
label
': '
Next
'
}
<i
aria-hidden=
"true"
class=
"fa fa-chevron-right"
></i>
...
...
core/tests.py
View file @
66980e59
from
django.test
import
TestCase
# Create your tests here.
core/utils
/create_child_instance_from_parent
.py
→
core/utils.py
View file @
66980e59
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from
__future__
import
absolute_import
from
pure_pagination
import
EmptyPage
from
pure_pagination
import
Paginator
from
django.shortcuts
import
render
def
create_child_instance_from_parent
(
child_cls
,
parent_instance
):
"""
...
...
@@ -34,3 +19,31 @@ def create_child_instance_from_parent(child_cls, parent_instance):
child_instance
.
__dict__
.
update
(
parent_instance
.
__dict__
)
child_instance
.
save
()
return
child_instance
def
render_paginate_list
(
request
,
object_list
,
template_name
):
"""
Render a paginated list of representatives
"""
pagination_limits
=
(
10
,
20
,
50
,
100
)
num_by_page
=
request
.
GET
.
get
(
'limit'
,
30
)
paginator
=
Paginator
(
object_list
,
num_by_page
)
number
=
request
.
GET
.
get
(
'page'
,
'1'
)
number
=
int
(
number
)
if
number
.
isdigit
()
else
1
try
:
page
=
paginator
.
page
(
number
)
except
EmptyPage
:
page
=
paginator
.
page
(
paginator
.
num_pages
)
context
=
{}
context
[
'paginator'
]
=
paginator
context
[
'page'
]
=
page
context
[
'object_list'
]
=
context
[
'page'
].
object_list
context
[
'pagination_limits'
]
=
pagination_limits
return
render
(
request
,
template_name
,
context
)
core/utils/__init__.py
deleted
100644 → 0
View file @
9bf87cb4
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from
create_child_instance_from_parent
import
create_child_instance_from_parent
from
render_paginate_list
import
render_paginate_list
core/utils/digg_paginator.py
deleted
100644 → 0
View file @
9bf87cb4
import
math
from
django.core.paginator
import
\
Paginator
,
QuerySetPaginator
,
Page
,
InvalidPage
__all__
=
(
'InvalidPage'
,
'ExPaginator'
,
'DiggPaginator'
,
'QuerySetDiggPaginator'
,
)
class
ExPaginator
(
Paginator
):
"""Adds a ``softlimit`` option to ``page()``. If True, querying a
page number larger than max. will not fail, but instead return the
last available page.
This is useful when the data source can not provide an exact count
at all times (like some search engines), meaning the user could
possibly see links to invalid pages at some point which we wouldn't
want to fail as 404s.
>>> items = range(1, 1000)
>>> paginator = ExPaginator(items, 10)
>>> paginator.page(1000)
Traceback (most recent call last):
InvalidPage: That page contains no results
>>> paginator.page(1000, softlimit=True)
<Page 100 of 100>
# [bug] graceful handling of non-int args
>>> paginator.page("str")
Traceback (most recent call last):
InvalidPage: That page number is not an integer
"""
def
_ensure_int
(
self
,
num
,
e
):
# see Django #7307
try
:
return
int
(
num
)
except
ValueError
:
raise
e
def
page
(
self
,
number
,
softlimit
=
False
):
try
:
return
super
(
ExPaginator
,
self
).
page
(
number
)
except
InvalidPage
,
e
:
number
=
self
.
_ensure_int
(
number
,
e
)
if
number
>
self
.
num_pages
and
softlimit
:
return
self
.
page
(
self
.
num_pages
,
softlimit
=
False
)
else
:
raise
e
class
DiggPaginator
(
ExPaginator
):
"""
Based on Django's default paginator, it adds "Digg-style" page ranges
with a leading block of pages, an optional middle block, and another
block at the end of the page range. They are available as attributes
on the page:
{# with: page = digg_paginator.page(1) #}
{% for num in page.leading_range %} ...
{% for num in page.main_range %} ...
{% for num in page.trailing_range %} ...
Additionally, ``page_range`` contains a nun-numeric ``False`` element
for every transition between two ranges.
{% for num in page.page_range %}
{% if not num %} ... {# literally output dots #}
{% else %}{{ num }}
{% endif %}
{% endfor %}
Additional arguments passed to the constructor allow customization of
how those bocks are constructed:
body=5, tail=2
[1] 2 3 4 5 ... 91 92
|_________| |___|
body tail
|_____|
margin
body=5, tail=2, padding=2
1 2 ... 6 7 [8] 9 10 ... 91 92
|_| |__|
^padding^
|_| |__________| |___|
tail body tail
``margin`` is the minimum number of pages required between two ranges; if
there are less, they are combined into one.
When ``align_left`` is set to ``True``, the paginator operates in a
special mode that always skips the right tail, e.g. does not display the
end block unless necessary. This is useful for situations in which the
exact number of items/pages is not actually known.
# odd body length
>>> print DiggPaginator(range(1,1000), 10, body=5).page(1)
1 2 3 4 5 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5).page(100)
1 2 ... 96 97 98 99 100
# even body length
>>> print DiggPaginator(range(1,1000), 10, body=6).page(1)
1 2 3 4 5 6 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=6).page(100)
1 2 ... 95 96 97 98 99 100
# leading range and main range are combined when close; note how
# we have varying body and padding values, and their effect.
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=2, margin=2).page(3)
1 2 3 4 5 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=6, padding=2, margin=2).page(4)
1 2 3 4 5 6 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=1, margin=2).page(6)
1 2 3 4 5 6 7 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=2, margin=2).page(7)
1 2 ... 5 6 7 8 9 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=1, margin=2).page(7)
1 2 ... 5 6 7 8 9 ... 99 100
# the trailing range works the same
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=2, margin=2, ).page(98)
1 2 ... 96 97 98 99 100
>>> print DiggPaginator(range(1,1000), 10, body=6, padding=2, margin=2, ).page(97)
1 2 ... 95 96 97 98 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=1, margin=2, ).page(95)
1 2 ... 94 95 96 97 98 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=2, margin=2, ).page(94)
1 2 ... 92 93 94 95 96 ... 99 100
>>> print DiggPaginator(range(1,1000), 10, body=5, padding=1, margin=2, ).page(94)
1 2 ... 92 93 94 95 96 ... 99 100
# all three ranges may be combined as well
>>> print DiggPaginator(range(1,151), 10, body=6, padding=2).page(7)
1 2 3 4 5 6 7 8 9 ... 14 15
>>> print DiggPaginator(range(1,151), 10, body=6, padding=2).page(8)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
>>> print DiggPaginator(range(1,151), 10, body=6, padding=1).page(8)
1 2 3 4 5 6 7 8 9 ... 14 15
# no leading or trailing ranges might be required if there are only
# a very small number of pages
>>> print DiggPaginator(range(1,80), 10, body=10).page(1)
1 2 3 4 5 6 7 8
>>> print DiggPaginator(range(1,80), 10, body=10).page(8)
1 2 3 4 5 6 7 8
>>> print DiggPaginator(range(1,12), 10, body=5).page(1)
1 2
# test left align mode
>>> print DiggPaginator(range(1,1000), 10, body=5, align_left=True).page(1)
1 2 3 4 5
>>> print DiggPaginator(range(1,1000), 10, body=5, align_left=True).page(50)
1 2 ... 48 49 50 51 52
>>> print DiggPaginator(range(1,1000), 10, body=5, align_left=True).page(97)
1 2 ... 95 96 97 98 99
>>> print DiggPaginator(range(1,1000), 10, body=5, align_left=True).page(100)
1 2 ... 96 97 98 99 100
# padding: default value
>>> DiggPaginator(range(1,1000), 10, body=10).padding
4
# padding: automatic reduction
>>> DiggPaginator(range(1,1000), 10, body=5).padding
2
>>> DiggPaginator(range(1,1000), 10, body=6).padding
2
# padding: sanity check
>>> DiggPaginator(range(1,1000), 10, body=5, padding=3)
Traceback (most recent call last):
ValueError: padding too large for body (max 2)
"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
body
=
kwargs
.
pop
(
'body'
,
10
)
self
.
tail
=
kwargs
.
pop
(
'tail'
,
2
)
self
.
align_left
=
kwargs
.
pop
(
'align_left'
,
False
)
self
.
margin
=
kwargs
.
pop
(
'margin'
,
4
)
# TODO: make the default relative to body?
# validate padding value
max_padding
=
int
(
math
.
ceil
(
self
.
body
/
2.0
)
-
1
)
self
.
padding
=
kwargs
.
pop
(
'padding'
,
min
(
4
,
max_padding
))
if
self
.
padding
>
max_padding
:
raise
ValueError
(
'padding too large for body (max %d)'
%
max_padding
)
super
(
DiggPaginator
,
self
).
__init__
(
*
args
,
**
kwargs
)
def
page
(
self
,
number
,
*
args
,
**
kwargs
):
"""Return a standard ``Page`` instance with custom, digg-specific
page ranges attached.
"""
page
=
super
(
DiggPaginator
,
self
).
page
(
number
,
*
args
,
**
kwargs
)
number
=
int
(
number
)
# we know this will work
# easier access
num_pages
,
body
,
tail
,
padding
,
margin
=
\
self
.
num_pages
,
self
.
body
,
self
.
tail
,
self
.
padding
,
self
.
margin
# put active page in middle of main range
main_range
=
map
(
int
,
[
math
.
floor
(
number
-
body
/
2.0
)
+
1
,
# +1 = shift odd body to right
math
.
floor
(
number
+
body
/
2.0
)])
# adjust bounds
if
main_range
[
0
]
<
1
:
main_range
=
map
(
abs
(
main_range
[
0
]
-
1
).
__add__
,
main_range
)
if
main_range
[
1
]
>
num_pages
:
main_range
=
map
((
num_pages
-
main_range
[
1
]).
__add__
,
main_range
)
# Determine leading and trailing ranges; if possible and appropriate,
# combine them with the main range, in which case the resulting main
# block might end up considerable larger than requested. While we
# can't guarantee the exact size in those cases, we can at least try
# to come as close as possible: we can reduce the other boundary to
# max padding, instead of using half the body size, which would
# otherwise be the case. If the padding is large enough, this will
# of course have no effect.
# Example:
# total pages=100, page=4, body=5, (default padding=2)
# 1 2 3 [4] 5 6 ... 99 100
# total pages=100, page=4, body=5, padding=1
# 1 2 3 [4] 5 ... 99 100
# If it were not for this adjustment, both cases would result in the
# first output, regardless of the padding value.
if
main_range
[
0
]
<=
tail
+
margin
:
leading
=
[]
main_range
=
[
1
,
max
(
body
,
min
(
number
+
padding
,
main_range
[
1
]))]
main_range
[
0
]
=
1
else
:
leading
=
range
(
1
,
tail
+
1
)
# basically same for trailing range, but not in ``left_align`` mode
if
self
.
align_left
:
trailing
=
[]
else
:
if
main_range
[
1
]
>=
num_pages
-
(
tail
+
margin
)
+
1
:
trailing
=
[]
if
not
leading
:
# ... but handle the special case of neither leading nor
# trailing ranges; otherwise, we would now modify the
# main range low bound, which we just set in the previous
# section, again.
main_range
=
[
1
,
num_pages
]
else
:
main_range
=
[
min
(
num_pages
-
body
+
1
,
max
(
number
-
padding
,
main_range
[
0
])),
num_pages
]
else
:
trailing
=
range
(
num_pages
-
tail
+
1
,
num_pages
+
1
)
# finally, normalize values that are out of bound; this basically
# fixes all the things the above code screwed up in the simple case
# of few enough pages where one range would suffice.
main_range
=
[
max
(
main_range
[
0
],
1
),
min
(
main_range
[
1
],
num_pages
)]
# make the result of our calculations available as custom ranges
# on the ``Page`` instance.
page
.
main_range
=
range
(
main_range
[
0
],
main_range
[
1
]
+
1
)
page
.
leading_range
=
leading
page
.
trailing_range
=
trailing
page
.
page_range
=
reduce
(
lambda
x
,
y
:
x
+
((
x
and
y
)
and
[
False
])
+
y
,
[
page
.
leading_range
,
page
.
main_range
,
page
.
trailing_range
])
page
.
__class__
=
DiggPage
return
page
class
DiggPage
(
Page
):
def
__str__
(
self
):
return
" ... "
.
join
(
filter
(
None
,
[
" "
.
join
(
map
(
str
,
self
.
leading_range
)),
" "
.
join
(
map
(
str
,
self
.
main_range
)),
" "
.
join
(
map
(
str
,
self
.
trailing_range
))]))
class
QuerySetDiggPaginator
(
DiggPaginator
,
QuerySetPaginator
):
pass
if
__name__
==
"__main__"
:
import
doctest
doctest
.
testmod
()
core/utils/render_paginate_list.py
deleted
100644 → 0
View file @
9bf87cb4
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from
__future__
import
absolute_import
from
django.core.paginator
import
EmptyPage
,
PageNotAnInteger
,
Paginator
from
.digg_paginator
import
DiggPaginator
from
django.shortcuts
import
render
def
render_paginate_list
(
request
,
object_list
,
template_name
):
"""
Render a paginated list of representatives
"""
pagination_limits
=
(
10
,
20
,
50
,
100
)
num_by_page
=
request
.
GET
.
get
(
'limit'
,
30
)
paginator
=
DiggPaginator
(
object_list
,
num_by_page
,
body
=
5
)
# paginator = Paginator(object_list, num_by_page)
page
=
request
.
GET
.
get
(
'page'
,
1
)
try
:
objects
=
paginator
.
page
(
page
)
except
PageNotAnInteger
:
objects
=
paginator
.
page
(
1
)
except
EmptyPage
:
objects
=
paginator
.
page
(
paginator
.
num_pages
)
context
=
{}
queries_without_page
=
request
.
GET
.
copy
()
if
'page'
in
queries_without_page
:
del
queries_without_page
[
'page'
]
context
[
'queries'
]
=
queries_without_page
context
[
'object_list'
]
=
objects
context
[
'paginator'
]
=
paginator
context
[
'pagination_limits'
]
=
pagination_limits
return
render
(
request
,
template_name
,
context
)
docs/administration.rst
0 → 100644
View file @
66980e59
Administrator guide
~~~~~~~~~~~~~~~~~~~
This guide targets the administrator who has deployed
the website in production.
Authentication in the admin backend
===================================
If you haven't already, create a super-administrator
account with command::
./manage.py createsuperuser
Then use this account to authenticate in the
administration backend located in ``/admin``.
User groups
===========
You should create a user group with all permissions
on:
- vote | recommendation
- positions | position
Creating a user
===============
To create a content administrator, create a user with:
- "Staff status": checked, otherwise the user won't be
able to authenticate in the administration backend,
- groups: at least the group created above.
Then, send the credentials to the user along with a link to :doc:`usage`.
docs/conf.py
View file @
66980e59
...
...
@@ -12,9 +12,6 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import
sys
import
os
import
shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
...
...
docs/deployment.rst
View file @
66980e59
...
...
@@ -91,3 +91,5 @@ to be executed, either do it manually::
OpenShift is fun, login with ssh and look around if you're curious, you'll be
able to recreate your app without much effort if you break it anyway.
Continue to :doc:`administration`.
docs/development.rst
View file @
66980e59
...
...
@@ -67,18 +67,6 @@ Then, install the package for development::
Running setup.py develop for political-memory
Successfully installed django-1.9 django-coffeescript-0.7.2 django-debug-toolbar-1.4 django-extensions-1.5.9 django-pdb-0.4.2 hamlpy-0.82.2 ijson-2.2 markdown-2.6.5 political-memory pygments-2.0.2 python-dateutil-2.4.2 pytz-2015.7 six-1.10.0 south-1.0.2 sqlparse-0.1.18 werkzeug-0.11.2
And install the requirements::
$ pip install -r requirements.txt
Collecting django<1.9,>=1.8 (from -r requirements.txt (line 1))
[output snipped for readability]
Using cached Django-1.8.7-py2.py3-none-any.whl
Running setup.py develop for django-representatives
Running setup.py develop for django-representatives-votes
Successfully installed amqp-1.4.8 anyjson-0.3.3 billiard-3.3.0.22 celery-3.1.19 django-1.8.7 django-adminplus-0.5 django-appconf-1.0.1 django-autocomplete-light-2.2.10 django-bootstrap3-6.2.2 django-celery-3.1.17 django-compressor-1.6 django-constance-1.1.1 django-datetime-widget-0.9.3 django-denorm-0.2.0 django-filter-0.11.0 django-picklefield-0.3.2 django-representatives django-representatives-votes django-taggit-0.17.5 django-uuidfield-0.5.0 djangorestframework-3.3.1 kombu-3.0.30 py-dateutil-2.2 pyprind-2.9.3 requests-2.8.1 slugify-0.0.1
Install NodeJS dependencies
===========================
...
...
@@ -124,14 +112,14 @@ Build the static files with gulp::
have gulp watching for changes and rebuilding static files
automatically.
Activate ``DEBUG``
==================
Activate ``
DJANGO_
DEBUG``
==================
=======
``DEBUG`` is disabled by default, the development server
won't run properly by
default thnen, to enable it export
the ``DEBUG`` variable in the current