Skip to content
Extraits de code Groupes Projets

Comparer les révisions

Les modifications sont affichées comme si la révision source était fusionnée avec la révision cible. En savoir plus sur la comparaison des révisions.

Source

Sélectionner le projet cible
No results found
Sélectionner une révision Git
  • 162-work-on-home-html
  • 166-codecov
  • 184-returning-meps-with-an-active-mandates-in-django-rql-filter
  • 189-design-v3-views
  • 194-used-solr-search-for-representative-instead-of-default-django-engine
  • 203-fix-deploy
  • compotista_downloader
  • development_features
  • docker
  • docker-compose-rewrite
  • docs_openshift
  • fixClient
  • flavien
  • flavienMain
  • graphene
  • i18n
  • locale
  • master
  • perf-rec
  • restructure
  • restructure_test
  • score_multiplier
  • search_update
  • test
  • update_test
  • design-v2
  • openshift-v0.1.77
  • openshift_build
28 résultats

Cible

Sélectionner le projet cible
  • la-quadrature-du-net/memopol/memopol
  • lnclt/political_memory
  • arthur/political_memory
  • agrausem/political_memory
  • periode/memopol
  • Anthony/memopol
  • Porkepix/memopol
  • jaster/memopol
  • luxcem/memopol
  • TAlone/memopol
10 résultats
Sélectionner une révision Git
  • 162-work-on-home-html
  • 166-codecov
  • 184-returning-meps-with-an-active-mandates-in-django-rql-filter
  • compotista_downloader
  • docker
  • fixClient
  • flavien
  • flavienMain
  • graphene
  • i18n
  • locale
  • master
  • perf-rec
  • restructure
  • restructure_test
  • score_multiplier
  • test
  • design-v2
  • openshift-v0.1.77
  • openshift_build
20 résultats
Afficher les modifications
Affichage de
avec 43031 ajouts et 7 suppressions
{% block active %}
Display :
{% if active_only %}
current legislature only /
<a href="{queries.urlencode}&active_only=0">
all data
</a>
{% else %}
<a href="{queries.urlencode}&active_only=1">
current legislature only
</a>
/ all data
{% endif %}
{% endblock %}
%span.grid-list-selector %span.grid-list-selector
%a{:href => '?display=grid'} %a{:href => '?={queries.urlencode}&display=grid'}<
<i class="fa fa-th"></i> <i class="fa fa-th"></i>
%a{:href => '?display=list'} %a{:href => '?={queries.urlencode}&display=list'}<
<i class="fa fa-th-list"></i> <i class="fa fa-th-list"></i>
{% block pagination %}
<nav>
<ul class="pagination pagination-sm">
{% if page_obj.has_previous %}
<li>
<a href="{{queries.urlencode}}&page=1" aria-label="First">
<i aria-hidden="true" class="fa fa-chevron-circle-left"></i>
</a>
</li>
<li>
<a href="{queries.urlencode}&page=={page_obj.previous_page_number}" aria-label="Previous">
<i aria-hidden="true" class="fa fa-chevron-left"></i>
</a>
</li>
{% endif %}
{% for p in page_range %}
{% if p %}
{% if p == page_obj.number %}
<li class="active">
<a href=''>
{{ p }}
</a>
</li>
{% else %}
<li>
<a href="{queries.urlencode}&page=={p}">
{{ p }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>
<a href=""{queries.urlencode}&page=={page_obj.next_page_number}" aria-label="Next">
<i aria-hidden="true" class="fa fa-chevron-right"></i>
</a>
</li>
<li>
<a href="{queries.urlencode}&page=={paginator.num_pages}" aria-label="Last">
<i aria-hidden="true" class="fa fa-chevron-circle-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
<div class=".count">
{% block count %}
Number of results : {{ paginator.count }}
{% if active_only != None %}
{% include 'core/blocks/active-filter.html' %}
{% endif %}
{% if sort != None %}
{% include 'core/blocks/sorting.html' %}
{% endif %}
{% endblock %}
{% block page-size %}
Number of displayed results :
{{ paginator.per_page }}
(
{% for limit in pagination_limits %}
<a href="{queries.urlencode}&paginate_by={{ limit }}">
{{ limit }}
</a>
{% if not forloop.last %}
/
{% endif %}
{% endfor %}
)
{% if grid_list %}
{% include 'core/blocks/grid-list.html' %}
{% endif %}
{% endblock %}
</div>
{% endblock %}
- if sort.fields|length > 0
.sorting-block
Order by :
- for field, label in sort.fields.items
- if sort.field == field
{{ label }}
- else
%a{'href': '?={queries.urlencode}&sort_by=={field}'}<
{{ label }}
- if not forloop.last
=" | "
&ndash;
- if sort.dir == 'asc'
ascending |
%a{'href': '?={queries.urlencode}&sort_dir=desc'}<
descending
- else
%a{'href': '?={queries.urlencode}&sort_dir=asc'}<
ascending
="| descending"
Fichier déplacé
# coding: utf-8
from copy import copy
from django import http
import unicodecsv as csv
class ThemeSelectionMixin(object):
"""
Mixin for views that allow selecting a theme
"""
def get(self, *args, **kwargs):
self.set_selected_theme()
return super(ThemeSelectionMixin, self).get(*args, **kwargs)
def set_selected_theme(self):
if 'selected_theme' in self.request.GET:
theme = self.request.GET['selected_theme']
self.request.session['selected_theme'] = \
theme if len(theme) else None
elif 'selected_theme' not in self.request.session:
self.request.session['selected_theme'] = None
def get_selected_theme(self):
if 'selected_theme' in self.request.session:
return self.request.session['selected_theme']
else:
return None
def get_context_data(self, **kwargs):
c = super(ThemeSelectionMixin, self).get_context_data(**kwargs)
c['selected_theme'] = self.get_selected_theme()
c['theme_querystring'] = copy(self.request.GET)
if 'selected_theme' in c['theme_querystring']:
del c['theme_querystring']['selected_theme']
return c
class ActiveLegislatureMixin(object):
"""
Mixin for views that can switch between active legislature and all data
"""
default_active_only = True
def get(self, *args, **kwargs):
self.set_active_only()
return super(ActiveLegislatureMixin, self).get(*args, **kwargs)
def override_active_only(self):
"""
Redefine this method to override active legislature selection
- return None to enable user choice
- return True or False to disable user choice and set active state
"""
return None
def set_active_only(self):
if 'active_only' in self.request.GET:
self.request.session['active_only'] = \
self.request.GET['active_only'] == '1'
elif 'active_only' not in self.request.session:
self.request.session['active_only'] = self.default_active_only
def get_active_only(self):
overriden = self.override_active_only()
if overriden is None:
if 'active_only' in self.request.session:
return self.request.session['active_only']
else:
return self.default_active_only
else:
return overriden
def get_context_data(self, **kwargs):
c = super(ActiveLegislatureMixin, self).get_context_data(**kwargs)
if self.override_active_only() is None:
c['active_only'] = self.get_active_only()
return c
class SortMixin(object):
"""
Mixin for views that allow sorting.
The sort_modes attribute should be defined to a dict as such:
{
'mode1': {
'order': 42,
'label': 'mode label',
'fields': ['-field1', 'field2', ...]
},
...
}
The sort_default attribute should contain the default sorting mode.
"""
sort_modes = {}
sort_default = None
sort_session_prefix = ''
def get(self, *args, **kwargs):
self.set_sorting()
return super(SortMixin, self).get(*args, **kwargs)
def _session_get_sort(self):
k = '%s_sort' % self.sort_session_prefix
return self.request.session[k]
def _session_set_sort(self, value):
k = '%s_sort' % self.sort_session_prefix
self.request.session[k] = value
def _session_sort_exists(self):
k = '%s_sort' % self.sort_session_prefix
return k in self.request.session
def set_sorting(self):
if 'sort' in self.request.GET:
self._session_set_sort(self.request.GET['sort'])
elif not self._session_sort_exists():
self._session_set_sort(self.sort_default)
if self._session_get_sort() not in self.sort_modes:
self._session_set_sort(self.sort_default)
def get_context_data(self, **kwargs):
c = super(SortMixin, self).get_context_data(**kwargs)
c['sort_querystring'] = copy(self.request.GET)
if 'sort' in c['sort_querystring']:
del c['sort_querystring']['sort']
c['sort'] = {
'modes': [{'id': k, 'label': v['label'], 'order': v['order']}
for k, v in self.sort_modes.iteritems()],
'mode': self._session_get_sort()
}
return c
def get_queryset(self):
qs = super(SortMixin, self).get_queryset()
if self._session_get_sort() in self.sort_modes:
mode = self.sort_modes[self._session_get_sort()]
qs = qs.order_by(*mode['fields'])
return qs
class PaginationMixin(object):
pagination_limits = (12, 24, 48, 96)
def get(self, *args, **kwargs):
self.set_paginate_by()
return super(PaginationMixin, self).get(*args, **kwargs)
def set_paginate_by(self):
if 'paginate_by' in self.request.GET:
self.request.session['paginate_by'] = \
self.request.GET['paginate_by']
elif 'paginate_by' not in self.request.session:
self.request.session['paginate_by'] = 12
def get_paginate_by(self, queryset):
return self.request.session['paginate_by']
def get_page_range(self, page):
pages = []
if page and page.paginator.num_pages != 1:
for i in page.paginator.page_range:
if page.number - 4 < i < page.number + 4:
pages.append(i)
return pages
def get_context_data(self, **kwargs):
c = super(PaginationMixin, self).get_context_data(**kwargs)
c['pagination_limits'] = self.pagination_limits
c['paginate_by'] = self.request.session['paginate_by']
c['page_range'] = self.get_page_range(c['page_obj'])
c['pagination_querystring'] = copy(self.request.GET)
if 'page' in c['pagination_querystring']:
del c['pagination_querystring']['page']
return c
class GridListMixin(object):
def set_session_display(self):
if self.request.GET.get('display') in ('grid', 'list'):
self.request.session['display'] = self.request.GET.get('display')
if 'display' not in self.request.session:
self.request.session['display'] = 'grid'
def get(self, *args, **kwargs):
self.set_session_display()
return super(GridListMixin, self).get(*args, **kwargs)
def get_template_names(self):
return [t.replace('_list', '_%s' % self.request.session['display'])
for t in super(GridListMixin, self).get_template_names()]
def get_context_data(self, **kwargs):
c = super(GridListMixin, self).get_context_data(**kwargs)
c['grid_list'] = True
return c
class CSVDownloadMixin(object):
def get_context_data(self, **kwargs):
c = super(CSVDownloadMixin, self).get_context_data(**kwargs)
c['csv'] = True
c['csv_querystring'] = copy(self.request.GET)
return c
def get_paginate_by(self, queryset):
if self.request.GET.get('csv', None) is None:
return super(CSVDownloadMixin, self).get_paginate_by(queryset)
return None
def render_to_csv_response(self, context, **kwargs):
response = http.HttpResponse(content_type='text/csv')
writer = csv.writer(response)
for result in self.get_csv_results(context, **kwargs):
writer.writerow(self.get_csv_row(result))
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % (
self.csv_name)
return response
def render_to_response(self, context, **kwargs):
if self.request.GET.get('csv', None) is None:
return super(CSVDownloadMixin, self).render_to_response(
context, **kwargs)
return self.render_to_csv_response(context, **kwargs)
Fichier déplacé
from rest_framework import routers
from rql_filter.backend import RQLFilterBackend as RQLBackend
from representatives.api import (
CountryViewSet,
ChamberViewSet,
ConstituencyViewSet,
GroupViewSet,
MandateViewSet,
RepresentativeViewSet,
)
from representatives_votes.api import (
DossierViewSet,
ProposalViewSet,
VoteViewSet,
)
from representatives_recommendations.api import (
RecommendationViewSet
)
from memopol_themes.api import (
ThemeViewSet
)
from memopol_scores.api import (
DossierScoreViewSet,
RepresentativeScoreViewSet,
ThemeScoreViewSet,
VoteScoreViewSet
)
class RQLDossierViewSet(DossierViewSet):
filter_backends = DossierViewSet.filter_backends + (RQLBackend,)
class RQLProposalViewSet(ProposalViewSet):
filter_backends = ProposalViewSet.filter_backends + (RQLBackend,)
class RQLVoteViewSet(VoteViewSet):
filter_backends = VoteViewSet.filter_backends + (RQLBackend,)
router = routers.DefaultRouter()
router.register('countries', CountryViewSet, 'api-country')
router.register('chambers', ChamberViewSet, 'api-chamber')
router.register('constituencies', ConstituencyViewSet, 'api-constituency')
router.register('dossiers', RQLDossierViewSet, 'api-dossier')
router.register('dossier_scores', DossierScoreViewSet, 'api-dossierscore')
router.register('groups', GroupViewSet, 'api-group')
router.register('mandates', MandateViewSet, 'api-mandate')
router.register('proposals', RQLProposalViewSet, 'api-proposal')
router.register('recommendations', RecommendationViewSet, 'api-recommendation')
router.register('representatives', RepresentativeViewSet,
'api-representative')
router.register('scores', RepresentativeScoreViewSet, 'api-score')
router.register('theme_scores', ThemeScoreViewSet, 'api-themescore')
router.register('themes', ThemeViewSet, 'api-themes')
router.register('vote_scores', VoteScoreViewSet, 'api-votescore')
router.register('votes', RQLVoteViewSet, 'api-vote')
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# starting with 'downloadFromGithub' at the bottom. # starting with 'downloadFromGithub' at the bottom.
# The last argument is expected to be a git ref (ie # The last argument is expected to be a git ref (ie
# a branch name, tag or commit-ish). # a branch name, tag or commit-ish).
# #
set -e set -e
...@@ -24,6 +24,10 @@ function downloadFromGithub() ...@@ -24,6 +24,10 @@ function downloadFromGithub()
local repo=$2 local repo=$2
local ref=$3 local ref=$3
if [ -d $dest ]; then
echo "* $repo ($ref) already downloaded, skipping"
return
fi
echo "* Downloading $repo ($ref) from Github..." echo "* Downloading $repo ($ref) from Github..."
wget -O temp.zip -q https://github.com/${repo}/archive/${ref}.zip wget -O temp.zip -q https://github.com/${repo}/archive/${ref}.zip
unzip -q temp.zip unzip -q temp.zip
...@@ -33,12 +37,12 @@ function downloadFromGithub() ...@@ -33,12 +37,12 @@ function downloadFromGithub()
set -e set -e
[ -d ${DEST} ] && rm -r ${DEST} if [ -z ${CI-} ]; then
[ -d ${DEST} ] && rm -r ${DEST}
fi
mkdir -p ${DEST} mkdir -p ${DEST}
downloadFromGithub jquery jquery/jquery 2.1.4 downloadFromGithub jquery jquery/jquery 2.1.4
downloadFromGithub fontawesome FortAwesome/Font-Awesome v4.3.0
downloadFromGithub flag-icon-css lipis/flag-icon-css 0.7.1 downloadFromGithub flag-icon-css lipis/flag-icon-css 0.7.1
downloadFromGithub bootstrap twbs/bootstrap v3.3.5
echo "* Done." echo "* Done."
from .forms import DossierSearchForm, RepresentativeSearchForm
from memopol_settings.models import Setting
def search_forms(request):
return {
'representative_search_form': RepresentativeSearchForm(request.GET),
'dossier_search_form': DossierSearchForm(request.GET)
}
def intro_text(request):
d = {}
for s in Setting.objects.filter(pk__in=['HOMEPAGE_INTRO_TEXT',
'HOMEPAGE_INSTANCE_TEXT']):
d[s.pk] = s.comment
return d
# coding: utf-8
import datetime
from django.db.models import Q
from django.utils.text import slugify
from django_filters import FilterSet, MethodFilter
from representatives.models import Representative
from representatives_votes.models import Dossier
from memopol_themes.models import Theme
class RepresentativeFilter(FilterSet):
search = MethodFilter(action='search_filter')
scoremin = MethodFilter(action='score_min_filter')
scoremax = MethodFilter(action='score_max_filter')
chamber = MethodFilter(action='chamber_filter')
country = MethodFilter(action='group_filter')
party = MethodFilter(action='group_filter')
delegation = MethodFilter(action='group_filter')
committee = MethodFilter(action='group_filter')
class Meta:
model = Representative
fields = ['search', 'chamber', 'country', 'party', 'delegation',
'committee']
def search_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(slug__icontains=slugify(value))
def chamber_filter(self, qs, value):
if len(value) == 0:
return qs
today = datetime.date.today()
return qs.filter(
Q(mandates__end_date__gte=today) |
Q(mandates__end_date__isnull=True),
mandates__group__chamber=value
)
def group_filter(self, qs, value):
if len(value) == 0:
return qs
today = datetime.date.today()
return qs.filter(
Q(mandates__end_date__gte=today) |
Q(mandates__end_date__isnull=True),
mandates__group=value
)
def score_min_filter(self, qs, value):
if len(value) == 0:
return qs
try:
return qs.filter(representative_score__score__gte=int(value))
except ValueError:
return qs
def score_max_filter(self, qs, value):
if len(value) == 0:
return qs
try:
return qs.filter(representative_score__score__lte=int(value))
except ValueError:
return qs
class DossierFilter(FilterSet):
search = MethodFilter(action='search_filter')
chamber = MethodFilter(action='chamber_filter')
class Meta:
model = Dossier
fields = ['search', 'chamber']
def search_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(Q(title__icontains=value) |
Q(reference__icontains=value) |
Q(documents__link__icontains=value))
def chamber_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(documents__chamber=value)
class ThemeFilter(FilterSet):
search = MethodFilter(action='search_filter')
class Meta:
model = Theme
fields = ['search']
def search_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(Q(name__icontains=value) |
Q(description__icontains=value))
Impossible d'afficher diff de source : il est trop volumineux. Options pour résoudre ce problème : voir le blob.
from django import forms
from dal import autocomplete, forward
from representatives.models import Chamber, Group
class RepresentativeSearchForm(forms.Form):
search = forms.CharField(
required=False,
label='Name',
widget=forms.TextInput(attrs={'placeholder': ''})
)
scoremin = forms.FloatField(
required=False,
label='Between',
widget=forms.NumberInput(attrs={'placeholder': 'Min. score'})
)
scoremax = forms.FloatField(
required=False,
label='and',
widget=forms.NumberInput(attrs={'placeholder': 'Max. score'})
)
chamber = forms.ModelChoiceField(
queryset=Chamber.objects.all(),
required=False,
widget=autocomplete.ModelSelect2(
url='chamber-autocomplete',
attrs={'data-html': 'true'}
)
)
country = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='country'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('country', 'kind'),),
attrs={'data-html': 'true'}
)
)
party = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='group'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('group', 'kind'),),
attrs={'data-html': 'true'}
)
)
committee = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='committee'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('committee', 'kind'),),
attrs={'data-html': 'true'}
)
)
delegation = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='delegation'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('delegation', 'kind'),),
attrs={'data-html': 'true'}
)
)
class DossierSearchForm(forms.Form):
search = forms.CharField(
required=False,
label='Name',
widget=forms.TextInput(attrs={'placeholder': ''})
)
chamber = forms.ModelChoiceField(
queryset=Chamber.objects.all(),
required=False,
widget=autocomplete.ModelSelect2(
url='chamber-autocomplete',
attrs={'data-html': 'true'}
)
)
from django.conf.urls import url
from django.core.urlresolvers import reverse
from django.views.generic.base import RedirectView
class TabRedirectView(RedirectView):
permanent = True
def get_redirect_url(self, *args, **kwargs):
if 'tab' in kwargs:
tab = kwargs.pop('tab')
return reverse('%s-%s' % (self.pattern_base, tab), args=args,
kwargs=kwargs)
else:
return reverse('%s-detail' % self.pattern_base, args=args,
kwargs=kwargs)
class RepresentativeListRedirect(RedirectView):
permanent = True
query_string = True
pattern_name = 'representative-list'
class RepresentativeDetailRedirect(TabRedirectView):
pattern_base = 'representative'
class DossierListRedirect(RedirectView):
permanent = True
query_string = True
pattern_name = 'dossier-list'
class DossierDetailRedirect(TabRedirectView):
pattern_base = 'dossier'
class ThemeListRedirect(RedirectView):
permanent = True
query_string = True
pattern_name = 'theme-list'
class ThemeDetailRedirect(RedirectView):
permanent = True
pattern_name = 'theme-detail'
urlpatterns = [
# Representative list
url(
r'^legislature/representative/$',
RepresentativeListRedirect.as_view(),
name='legacy-representative-list'
),
# Representative detail
url(
r'^legislature/representative/(?P<slug>[-\w]+)/$',
RepresentativeDetailRedirect.as_view(),
name='legacy-representative-detail'
),
url(
r'^legislature/representative/(?P<slug>[-\w]+)/(?P<tab>\w+)/$',
RepresentativeDetailRedirect.as_view(),
name='legacy-representative-detail'
),
# Dossier list
url(
r'^votes/dossier/$',
DossierListRedirect.as_view(),
name='legacy-dossier-list'
),
# Dossier detail
url(
r'^votes/dossier/(?P<pk>\d+)/$',
DossierDetailRedirect.as_view(),
name='legacy-dossier-detail'
),
url(
r'^votes/dossier/(?P<pk>\d+)/(?P<tab>\w+)/$',
DossierDetailRedirect.as_view(),
name='legacy-dossier-detail'
),
# Theme list
url(
r'^theme/$',
ThemeListRedirect.as_view(),
name='legacy-theme-list'
),
# Theme detail
url(
r'^theme/(?P<slug>[-\w]+)/$',
ThemeDetailRedirect.as_view(),
name='legacy-theme-detail'
),
]
"""
Memopol local settings example.
Edit and rename to local_settings.py to use.
"""
import os
from socket import gethostname
DATA_DIR = 'data'
LOG_DIR = 'log'
PUBLIC_DIR = 'wsgi/static'
DATABASES = {
'default': {
'NAME': 'memopol',
'USER': 'memopol',
'PASSWORD': 'memopol',
'HOST': 'localhost',
'PORT': '5432',
'ENGINE': 'django.db.backends.postgresql_psycopg2',
}
}
ALLOWED_HOSTS = [
gethostname(),
]
SITE_ID = 1
SITE_NAME = 'Memopol'
SITE_DOMAIN = gethostname()
ORGANIZATION_NAME = 'Memopol'
...@@ -2,9 +2,12 @@ ...@@ -2,9 +2,12 @@
import os import os
import sys import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "memopol.settings")
def main():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "memopol.settings")
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
from haystack import indexes
from representatives.models import Representative
class RepresentativeIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.EdgeNgramField(document=True, use_template=True)
slug = indexes.CharField(model_attr='slug', faceted=True)
first_name = indexes.CharField(model_attr='first_name', faceted=True)
last_name = indexes.CharField(model_attr='last_name', faceted=True)
full_name = indexes.EdgeNgramField(model_attr='full_name')
ascii_name = indexes.NgramField(model_attr='ascii_name')
def get_model(self):
return Representative
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
""" """
Django settings for memopol project. Django settings for memopol project.
For more information on this file, see Uses the following environment variables:
https://docs.djangoproject.com/en/1.7/topics/settings/ - DJANGO_DEBUG: set to "true" to enable debugging (default false)
- DJANGO_LANGUAGE_CODE: language (default en-us)
For the full list of settings and their values, see - DJANGO_LOG_LEVEL: log leve (defaults to INFO or DEBUG if debug enabled)
https://docs.djangoproject.com/en/1.7/ref/settings/ - DJANGO_ALLOWED_HOSTS: comma-separated additional allowed hosts (default none)
""" """
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
from socket import gethostname from socket import gethostname
from django.conf import global_settings from django.conf import global_settings
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = os.environ.get('OPENSHIFT_DATA_DIR', 'data')
if not os.path.exists(DATA_DIR):
os.makedirs(DATA_DIR)
LOG_DIR = os.environ.get('OPENSHIFT_LOG_DIR', 'log')
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
PUBLIC_DIR = os.path.join(
os.environ.get(
'OPENSHIFT_REPO_DIR',
''),
'wsgi/static')
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_FILE = os.path.join(DATA_DIR, 'secret.txt')
if not os.path.exists(SECRET_FILE):
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
with open(SECRET_FILE, 'w+') as f:
f.write(get_random_string(50, chars))
with open(SECRET_FILE, 'r') as f:
SECRET_KEY = f.read()
###############################################################################
#
# Base memopol settings
#
DEBUG = os.environ.get('DJANGO_DEBUG', False) BASE_DIR = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_DEBUG = DEBUG DEBUG = os.getenv('DJANGO_DEBUG', False)
LOG_LEVEL = os.environ.get('DJANGO_LOG_LEVEL', 'INFO')
if SECRET_KEY == 'notsecret' and not DEBUG: if hasattr(DEBUG, 'lower') and DEBUG.lower() in ('false', '0', 'none'):
raise Exception('Please export DJANGO_SECRET_KEY or DEBUG') DEBUG = False # Support none-ish values
ALLOWED_HOSTS = [ #
gethostname(), # Main Django config
] #
DNS = os.environ.get('OPENSHIFT_APP_DNS', None),
if DNS:
ALLOWED_HOSTS += DNS
if 'DJANGO_ALLOWED_HOSTS' in os.environ: ROOT_URLCONF = 'memopol.urls'
ALLOWED_HOSTS += os.environ.get('DJANGO_ALLOWED_HOSTS').split(',') WSGI_APPLICATION = 'memopol.wsgi.application'
REDIS_DB = os.environ.get('REDIS_DB', 1) #
ORGANIZATION_NAME = os.environ.get('ORGANIZATION', 'Memopol Demo') # Apps
#
INSTALLED_APPS = ( INSTALLED_APPS = (
# 'django.contrib.admin', 'dal',
'autocomplete_light', 'dal_select2',
'suit', 'suit',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
...@@ -78,21 +48,29 @@ INSTALLED_APPS = ( ...@@ -78,21 +48,29 @@ INSTALLED_APPS = (
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.humanize', 'django.contrib.humanize',
'django.contrib.sites',
# 3rd party apps # 3rd party apps
'compressor', 'compressor',
'constance',
'constance.backends.database',
'bootstrap3', 'bootstrap3',
'datetimewidget', 'datetimewidget',
'django_filters', 'django_filters',
'fontawesome',
'rest_framework',
'taggit', 'taggit',
# --- 'haystack',
'nested_admin',
# memopol apps
'core', 'core',
'memopol',
'memopol_scores',
'memopol_settings',
'memopol_themes',
'representatives', 'representatives',
'representatives_votes', 'representatives_votes',
'legislature', 'representatives_recommendations',
'votes', 'representatives_positions',
'positions',
) )
if DEBUG: if DEBUG:
...@@ -103,6 +81,18 @@ if DEBUG: ...@@ -103,6 +81,18 @@ if DEBUG:
else: else:
INSTALLED_APPS += ('debug_toolbar',) INSTALLED_APPS += ('debug_toolbar',)
try:
import django_extensions # noqa
except:
pass
else:
INSTALLED_APPS += ('django_extensions',)
#
# Middleware
#
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
...@@ -111,128 +101,80 @@ MIDDLEWARE_CLASSES = ( ...@@ -111,128 +101,80 @@ MIDDLEWARE_CLASSES = (
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
) )
ROOT_URLCONF = 'memopol.urls' if 'debug_toolbar' in INSTALLED_APPS:
MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
#
# Sessions
#
WSGI_APPLICATION = 'memopol.wsgi.application' SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = {
'default': {
'NAME': os.environ.get('DJANGO_DATABASE_DEFAULT_NAME', 'db.sqlite'),
'USER': os.environ.get('DJANGO_DATABASE_DEFAULT_USER', ''),
'PASSWORD': os.environ.get('DJANGO_DATABASE_DEFAULT_PASSWORD', ''),
'HOST': os.environ.get('DJANGO_DATABASE_DEFAULT_HOST', ''),
'PORT': os.environ.get('DJANGO_DATABASE_DEFAULT_PORT', ''),
'ENGINE': os.environ.get('DJANGO_DATABASE_DEFAULT_ENGINE',
'django.db.backends.sqlite3'),
}
}
if 'OPENSHIFT_DATA_DIR' in os.environ:
DATABASES['default']['NAME'] = os.path.join(DATA_DIR, 'db.sqlite')
if 'OPENSHIFT_POSTGRESQL_DB_HOST' in os.environ:
DATABASES['default']['NAME'] = os.environ['OPENSHIFT_APP_NAME']
DATABASES['default']['USER'] = os.environ[
'OPENSHIFT_POSTGRESQL_DB_USERNAME']
DATABASES['default']['PASSWORD'] = os.environ[
'OPENSHIFT_POSTGRESQL_DB_PASSWORD']
DATABASES['default']['HOST'] = os.environ['OPENSHIFT_POSTGRESQL_DB_HOST']
DATABASES['default']['PORT'] = os.environ['OPENSHIFT_POSTGRESQL_DB_PORT']
DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2'
#
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/ #
LANGUAGE_CODE = os.environ.get('DJANGO_LANGUAGE_CODE', 'en-us') LANGUAGE_CODE = os.environ.get('DJANGO_LANGUAGE_CODE', 'en-us')
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'
USE_I18N = True USE_I18N = True
USE_L10N = True USE_L10N = True
USE_TZ = True USE_TZ = True
#
# Templating
#
# Static files (CSS, JavaScript, Images) TEMPLATE_DEBUG = DEBUG
# https://docs.djangoproject.com/en/1.7/howto/static-files/ TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_URL = '/static/'
COMPRESS_ROOT = 'static/'
if DATA_DIR:
MEDIA_URL = '/static/media/'
MEDIA_ROOT = os.path.join(DATA_DIR, 'media')
COMPRESS_ROOT = os.path.join(DATA_DIR, 'compress')
if PUBLIC_DIR:
STATIC_URL = '/static/collected/'
STATIC_ROOT = os.path.join(PUBLIC_DIR, 'collected')
# HAML Templates
# https://github.com/jessemiller/hamlpy
TEMPLATE_DIRS = (
'core/templates',
)
TEMPLATE_LOADERS = ( TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader', 'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader', 'django.template.loaders.app_directories.Loader',
'hamlpy.template.loaders.HamlPyFilesystemLoader',
'hamlpy.template.loaders.HamlPyAppDirectoriesLoader',
)
"""
TEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'hamlpy.template.loaders.HamlPyFilesystemLoader',
'hamlpy.template.loaders.HamlPyAppDirectoriesLoader',
)),
) )
"""
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'constance.context_processors.config',
'django.template.context_processors.request', 'django.template.context_processors.request',
'memopol.context_processors.search_forms',
'memopol.context_processors.intro_text'
) )
# Static files finders #
# Cache
#
if DEBUG:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
#
# Static files and related settings
#
STATIC_URL = '/static/collected/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# Compressor finder
'compressor.finders.CompressorFinder', 'compressor.finders.CompressorFinder',
) )
# Use compressor even in debug MEDIA_URL = '/static/media/'
COMPRESS_ENABLED = False COMPRESS_ENABLED = False
if os.environ.get('OPENSHIFT_LOG_DIR', None): #
# Enable offline compression on openshift # Logging
COMPRESS_ENABLED = True #
COMPRESS_OFFLINE = True
COMPRESS_PRECOMPILERS = (
# ('text/coffeescript', 'coffee --compile --stdio'),
('text/less', 'lesscpy {infile}'),
# ('text/x-sass', 'sass {infile} {outfile}'),
# ('text/x-scss', 'sass --scss {infile} {outfile}'),
# ('text/stylus', 'stylus < {infile} > {outfile}'),
# ('text/foobar', 'path.to.MyPrecompilerFilter'),
)
LOG_LEVEL = os.environ.get('DJANGO_LOG_LEVEL', 'DEBUG' if DEBUG else 'INFO')
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
...@@ -258,6 +200,14 @@ LOGGING = { ...@@ -258,6 +200,14 @@ LOGGING = {
'handlers': ['console'], 'handlers': ['console'],
'level': LOG_LEVEL, 'level': LOG_LEVEL,
}, },
'representatives_positions': {
'handlers': ['console'],
'level': LOG_LEVEL
},
'representatives_recommendations': {
'handlers': ['console'],
'level': LOG_LEVEL
},
'representatives_votes': { 'representatives_votes': {
'handlers': ['console'], 'handlers': ['console'],
'level': LOG_LEVEL, 'level': LOG_LEVEL,
...@@ -265,16 +215,112 @@ LOGGING = { ...@@ -265,16 +215,112 @@ LOGGING = {
}, },
} }
###############################################################################
#
# Local settings
#
#
# Defaults
#
DATA_DIR = 'data'
LOG_DIR = 'log'
PUBLIC_DIR = 'wsgi/static'
DATABASES = {
'default': {
'NAME': os.getenv('DB_NAME', 'memopol'),
'USER': os.getenv('DB_USER', 'memopol'),
'PASSWORD': os.getenv('DB_PASSWORD', 'memopol'),
'HOST': os.getenv('DB_HOST', 'localhost'),
'PORT': os.getenv('DB_PORT', '5432'),
'ENGINE': 'django.db.backends.postgresql_psycopg2',
}
}
ALLOWED_HOSTS = [
gethostname(),
]
SITE_ID = 1
SITE_NAME = 'Memopol'
SITE_DOMAIN = gethostname()
ORGANIZATION_NAME = 'Memopol'
#
# Import local overrides
#
try:
from local_settings import * # noqa
except ImportError:
pass
#
# Local directories
#
if not os.path.exists(DATA_DIR):
os.makedirs(DATA_DIR)
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
MEDIA_ROOT = os.path.join(DATA_DIR, 'media')
COMPRESS_ROOT = os.path.join(DATA_DIR, 'compress')
STATIC_ROOT = os.path.join(PUBLIC_DIR, 'collected')
if DEBUG: if DEBUG:
LOGGING['handlers']['debug'] = { LOGGING['handlers']['debug'] = {
'level': 'DEBUG', 'level': 'DEBUG',
'class': 'logging.FileHandler', 'class': 'logging.FileHandler',
'filename': os.path.join(LOG_DIR, 'debug.log'), 'filename': os.path.join(LOG_DIR, 'debug.log'),
} }
for logger in LOGGING['loggers'].values(): for logger in LOGGING['loggers'].values():
logger['handlers'].append('debug') logger['handlers'].append('debug')
#
# Secret file
#
SECRET_FILE = os.path.join(DATA_DIR, 'secret.txt')
if not os.path.exists(SECRET_FILE):
# Create random secret on first execution
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
with open(SECRET_FILE, 'w+') as f:
f.write(get_random_string(50, chars))
with open(SECRET_FILE, 'r') as f:
SECRET_KEY = f.read()
#
# Add allowed hosts from environment
# Automaticaly add 127.0.0.1 in ALLOWED_HOSTS on DEBUG
#
if 'DJANGO_ALLOWED_HOSTS' in os.environ:
ALLOWED_HOSTS += os.environ.get('DJANGO_ALLOWED_HOSTS').split(',')
if DEBUG:
ALLOWED_HOSTS += ['127.0.0.1', 'localhost']
#
# Raven configuration
#
# Put a 'sentry' file with Sentry DSN in DATA_DIR to enable.
# Optionally, put an empty 'sentry.404' in DATA_DIR to log 404s.
#
RAVEN_FILE = os.path.join(DATA_DIR, 'sentry') RAVEN_FILE = os.path.join(DATA_DIR, 'sentry')
RAVEN_404_FILE = os.path.join(DATA_DIR, 'sentry.404')
if os.path.exists(RAVEN_FILE): if os.path.exists(RAVEN_FILE):
INSTALLED_APPS += ('raven.contrib.django.raven_compat',) INSTALLED_APPS += ('raven.contrib.django.raven_compat',)
...@@ -288,15 +334,28 @@ if os.path.exists(RAVEN_FILE): ...@@ -288,15 +334,28 @@ if os.path.exists(RAVEN_FILE):
'propagate': False, 'propagate': False,
} }
with open(RAVEN_FILE, 'r') as f: if os.path.exists(RAVEN_404_FILE):
RAVEN_CONFIG = { RAVEN_MIDDLEWARE = (
'dsn': f.read().strip() 'raven.contrib.django.raven_compat.middleware.Sentry404CatchMiddleware', # noqa
} )
CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' MIDDLEWARE_CLASSES = RAVEN_MIDDLEWARE + MIDDLEWARE_CLASSES
CONSTANCE_CONFIG = { with open(RAVEN_FILE, 'r') as f:
'USE_COUNTRY': (True, 'Use country for representative'), RAVEN_CONFIG = {'dsn': f.read().strip()}
'MAIN_GROUP_KIND': ('group', 'Main group kind'),
'ORGANIZATION_NAME': ('La Quadrature du Net', 'Organization name'), # Haystack with Solr config
} if not DEBUG:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
'URL': 'http://127.0.0.1:8080/solr',
},
}
else:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
},
}
Fichier déplacé