add compatibility with python 3 (#25486)

This commit is contained in:
Frédéric Péters 2018-07-25 14:28:05 +02:00
parent 9751386861
commit 67931d2d78
39 changed files with 133 additions and 98 deletions

View File

@ -16,6 +16,7 @@
from django import template from django import template
from django.db.models.fields.files import ImageFieldFile from django.db.models.fields.files import ImageFieldFile
from django.utils import six
from sorl.thumbnail.shortcuts import get_thumbnail from sorl.thumbnail.shortcuts import get_thumbnail
@ -36,7 +37,7 @@ def asset_url(*args, **kwargs):
asset = asset_object asset = asset_object
break break
if isinstance(asset_object, basestring): if isinstance(asset_object, six.string_types):
try: try:
asset = Asset.objects.get(key=asset_object).asset asset = Asset.objects.get(key=asset_object).asset
break break

View File

@ -16,12 +16,11 @@
import datetime import datetime
import math import math
import urllib
from django.conf import settings from django.conf import settings
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.dateparse import parse_datetime from django.utils.dateparse import parse_datetime
from django.utils.http import urlencode
from django.utils.timezone import localtime, make_aware from django.utils.timezone import localtime, make_aware
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -39,7 +38,7 @@ def get_wcs_services():
def get_chrono_service(): def get_chrono_service():
for chrono_key, chrono_site in get_services('chrono').iteritems(): for chrono_key, chrono_site in get_services('chrono').items():
if not chrono_site.get('secondary', True): if not chrono_site.get('secondary', True):
chrono_site['slug'] = chrono_key chrono_site['slug'] = chrono_key
return chrono_site return chrono_site
@ -128,7 +127,7 @@ def get_form_url_with_params(cell, data):
} }
wcs_key, wcs_slug = cell.formdef_reference.split(':') wcs_key, wcs_slug = cell.formdef_reference.split(':')
wcs = get_wcs_services().get(wcs_key) wcs = get_wcs_services().get(wcs_key)
url = '%s%s/?%s' % (wcs['url'], wcs_slug, urllib.urlencode(session_vars)) url = '%s%s/?%s' % (wcs['url'], wcs_slug, urlencode(session_vars))
return url return url
@ -189,7 +188,7 @@ class Calendar(object):
"""return the first available slot that has enough """return the first available slot that has enough
consecutive available slots to be allowed for booking consecutive available slots to be allowed for booking
""" """
required_contiguous_slots = self.min_duration.seconds / self.offset.seconds required_contiguous_slots = self.min_duration.seconds // self.offset.seconds
for day in self.days: for day in self.days:
slots = day.slots slots = day.slots
for idx in range(len(slots) - required_contiguous_slots): for idx in range(len(slots) - required_contiguous_slots):

View File

@ -16,6 +16,7 @@
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.encoding import force_text
from django.views.generic import View, DetailView from django.views.generic import View, DetailView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
@ -36,7 +37,7 @@ class BookingView(SingleObjectMixin, View):
try: try:
form.is_valid() form.is_valid()
except ValueError as exc: except ValueError as exc:
messages.error(request, exc.message) messages.error(request, force_text(exc))
redirect_url = '%s?%s' % ( redirect_url = '%s?%s' % (
cell.page.get_online_url(), request.GET.urlencode()) cell.page.get_online_url(), request.GET.urlencode())
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)

View File

@ -21,7 +21,7 @@ from combo.utils import requests
def get_passerelle_service(): def get_passerelle_service():
if hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get('passerelle'): if hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get('passerelle'):
return settings.KNOWN_SERVICES['passerelle'].values()[0] return list(settings.KNOWN_SERVICES['passerelle'].values())[0]
def is_family_enabled(): def is_family_enabled():
return get_passerelle_service() and hasattr(settings, 'FAMILY_SERVICE') return get_passerelle_service() and hasattr(settings, 'FAMILY_SERVICE')

View File

@ -35,5 +35,5 @@ class Command(BaseCommand):
for regie in Regie.objects.exclude(webservice_url=''): for regie in Regie.objects.exclude(webservice_url=''):
try: try:
regie.notify_new_remote_invoices() regie.notify_new_remote_invoices()
except Exception, e: except Exception as e:
logger.exception('error while notifying new remote invoices: %s', e) logger.exception('error while notifying new remote invoices: %s', e)

View File

@ -20,6 +20,7 @@ from dateutil import parser as date_parser
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from django.db.models import Q from django.db.models import Q
from django.utils import six
from django.utils.timezone import make_aware from django.utils.timezone import make_aware
from django.views.generic import (CreateView, UpdateView, ListView, from django.views.generic import (CreateView, UpdateView, ListView,
DeleteView, TemplateView) DeleteView, TemplateView)
@ -93,7 +94,10 @@ def download_transactions_csv(request):
str(transaction.amount)] str(transaction.amount)]
for item in transaction.items.all(): for item in transaction.items.all():
row.extend([item.subject, str(item.amount)]) row.extend([item.subject, str(item.amount)])
writer.writerow([unicode(x).encode('utf-8') for x in row]) if six.PY3:
writer.writerow([x for x in row])
else:
writer.writerow([unicode(x).encode('utf-8') for x in row])
return response return response

View File

@ -19,7 +19,6 @@
import datetime import datetime
import json import json
import logging import logging
import urlparse
from decimal import Decimal from decimal import Decimal
@ -36,8 +35,10 @@ from django.utils import timezone, dateparse
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.utils.encoding import python_2_unicode_compatible
from django.utils.formats import localize from django.utils.formats import localize
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.six.moves.urllib import parse as urlparse
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.template.loader import render_to_string from django.template.loader import render_to_string
@ -84,6 +85,7 @@ def build_remote_item(data, regie):
no_online_payment_reason=data.get('no_online_payment_reason')) no_online_payment_reason=data.get('no_online_payment_reason'))
@python_2_unicode_compatible
class Regie(models.Model): class Regie(models.Model):
label = models.CharField(verbose_name=_('Label'), max_length=64) label = models.CharField(verbose_name=_('Label'), max_length=64)
slug = models.SlugField(unique=True, verbose_name=_('Identifier'), slug = models.SlugField(unique=True, verbose_name=_('Identifier'),
@ -127,7 +129,7 @@ class Regie(models.Model):
def natural_key(self): def natural_key(self):
return (self.slug,) return (self.slug,)
def __unicode__(self): def __str__(self):
return self.label return self.label
def get_text_on_success(self): def get_text_on_success(self):
@ -271,7 +273,7 @@ class Regie(models.Model):
return return
pending_invoices = self.get_remote_pending_invoices() pending_invoices = self.get_remote_pending_invoices()
notification_ids = [] notification_ids = []
for uuid, items in pending_invoices.iteritems(): for uuid, items in pending_invoices.items():
try: try:
user = UserSAMLIdentifier.objects.get(name_id=uuid).user user = UserSAMLIdentifier.objects.get(name_id=uuid).user
except UserSAMLIdentifier.DoesNotExist: except UserSAMLIdentifier.DoesNotExist:
@ -534,7 +536,7 @@ class LingoBasketCell(CellBase):
for items in regies.values(): for items in regies.values():
items['total'] = sum([x.amount for x in items['items']]) items['total'] = sum([x.amount for x in items['items']])
context['regies'] = regies.values() context['regies'] = sorted(regies.values(), key=lambda x: x['regie'].label)
return basket_template.render(context) return basket_template.render(context)

View File

@ -238,7 +238,7 @@ class ValidateTransactionApiView(View):
except eopayment.ResponseError as e: except eopayment.ResponseError as e:
logger.error(u'failed in validation operation: %s', e) logger.error(u'failed in validation operation: %s', e)
response = HttpResponse(content_type='application/json') response = HttpResponse(content_type='application/json')
response.write(json.dumps({'err': 1, 'e': unicode(e)})) response.write(json.dumps({'err': 1, 'e': force_text(e)}))
return response return response
logger.info(u'bank validation result: %r', result) logger.info(u'bank validation result: %r', result)
@ -279,7 +279,7 @@ class CancelTransactionApiView(View):
except eopayment.ResponseError as e: except eopayment.ResponseError as e:
logger.error(u'failed in cancel operation: %s', e) logger.error(u'failed in cancel operation: %s', e)
response = HttpResponse(content_type='application/json') response = HttpResponse(content_type='application/json')
response.write(json.dumps({'err': 1, 'e': unicode(e)})) response.write(json.dumps({'err': 1, 'e': force_text(e)}))
return response return response
logger.info(u'bank cancellation result: %r', result) logger.info(u'bank cancellation result: %r', result)
@ -414,7 +414,7 @@ class PaymentView(View):
'eopayment_order_id': smart_text(payment_response.order_id), 'eopayment_order_id': smart_text(payment_response.order_id),
'eopayment_response': repr(payment_response), 'eopayment_response': repr(payment_response),
} }
for k, v in payment_response.bank_data.iteritems(): for k, v in payment_response.bank_data.items():
extra_info['eopayment_bank_data_' + k] = smart_text(v) extra_info['eopayment_bank_data_' + k] = smart_text(v)
if not payment_response.signed and not payment_response.result == eopayment.CANCELLED: if not payment_response.signed and not payment_response.result == eopayment.CANCELLED:
# we accept unsigned cancellation requests as some platforms do # we accept unsigned cancellation requests as some platforms do
@ -494,9 +494,9 @@ class CallbackView(PaymentView):
try: try:
self.handle_response(request, backend_response, **kwargs) self.handle_response(request, backend_response, **kwargs)
except UnknownPaymentException as e: except UnknownPaymentException as e:
raise Http404(unicode(e)) raise Http404(force_text(e))
except PaymentException as e: except PaymentException as e:
return HttpResponseBadRequest(unicode(e)) return HttpResponseBadRequest(force_text(e))
return HttpResponse() return HttpResponse()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):

View File

@ -18,6 +18,8 @@ import json
from django.core import serializers from django.core import serializers
from django.db import models from django.db import models
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
@ -88,6 +90,7 @@ class MapLayerManager(models.Manager):
return self.get(slug=slug) return self.get(slug=slug)
@python_2_unicode_compatible
class MapLayer(models.Model): class MapLayer(models.Model):
objects = MapLayerManager() objects = MapLayerManager()
@ -123,7 +126,7 @@ class MapLayer(models.Model):
self.slug = slug self.slug = slug
super(MapLayer, self).save(*args, **kwargs) super(MapLayer, self).save(*args, **kwargs)
def __unicode__(self): def __str__(self):
return self.label return self.label
def natural_key(self): def natural_key(self):
@ -204,7 +207,7 @@ class MapLayer(models.Model):
def match(feature): def match(feature):
for geo_property in feature['properties'].values(): for geo_property in feature['properties'].values():
if not isinstance(geo_property, basestring): if not isinstance(geo_property, six.string_types):
continue continue
if query in slugify(geo_property): if query in slugify(geo_property):
return True return True

View File

@ -14,13 +14,12 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from urlparse import urlparse
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.db import connection from django.db import connection
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.utils import translation from django.utils import translation
from django.utils.six.moves.urllib.parse import urlparse
from combo.apps.momo.utils import generate_manifest, GenerationError, GenerationInfo from combo.apps.momo.utils import generate_manifest, GenerationError, GenerationInfo
@ -46,5 +45,5 @@ class Command(BaseCommand):
raise CommandError(e.message) raise CommandError(e.message)
except GenerationInfo as e: except GenerationInfo as e:
if int(kwargs.get('verbosity')) > 0: if int(kwargs.get('verbosity')) > 0:
print e.message print(e.message)
translation.deactivate() translation.deactivate()

View File

@ -84,7 +84,7 @@ class MomoIconCell(CellBase):
def get_default_form_class(self): def get_default_form_class(self):
sorted_icons = self._meta.get_field('icon').choices sorted_icons = self._meta.get_field('icon').choices
sorted_icons.sort(lambda x, y: cmp(x[1], y[1])) sorted_icons.sort(key=lambda x: x[1])
return model_forms.modelform_factory(self.__class__, return model_forms.modelform_factory(self.__class__,
fields=['icon', 'style', 'description', 'embed_page'], fields=['icon', 'style', 'description', 'embed_page'],
widgets={'icon': Select(choices=sorted_icons)}) widgets={'icon': Select(choices=sorted_icons)})

View File

@ -17,6 +17,7 @@
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.encoding import force_text
from django.views.generic import TemplateView, UpdateView from django.views.generic import TemplateView, UpdateView
from .models import MomoOptions from .models import MomoOptions
@ -30,9 +31,9 @@ def generate(request, **kwargs):
try: try:
generate_manifest(request) generate_manifest(request)
except GenerationError as e: except GenerationError as e:
messages.error(request, e.message) messages.error(request, force_text(e))
except GenerationInfo as e: except GenerationInfo as e:
messages.info(request, e.message) messages.info(request, force_text(e))
return HttpResponseRedirect(reverse('momo-manager-homepage')) return HttpResponseRedirect(reverse('momo-manager-homepage'))

View File

@ -33,7 +33,7 @@ class NewslettersManageForm(forms.Form):
self.cleaned_data = {} self.cleaned_data = {}
try: try:
newsletters = self.instance.get_newsletters() newsletters = self.instance.get_newsletters()
except Exception, e: except Exception as e:
self.add_error(None, _('An error occured while getting newsletters. Please try later.')) self.add_error(None, _('An error occured while getting newsletters. Please try later.'))
logger.error('Error occured while getting newsletters: %r', e) logger.error('Error occured while getting newsletters: %r', e)
return return
@ -46,7 +46,7 @@ class NewslettersManageForm(forms.Form):
self.params['mobile'] = self.request.session['mellon_session'].get('mobile', '') self.params['mobile'] = self.request.session['mellon_session'].get('mobile', '')
try: try:
subscriptions = self.instance.get_subscriptions(self.user, **self.params) subscriptions = self.instance.get_subscriptions(self.user, **self.params)
except Exception, e: except Exception as e:
self.add_error(None, _('An error occured while getting subscriptions. Please try later.')) self.add_error(None, _('An error occured while getting subscriptions. Please try later.'))
logger.error('Error occured while getting subscriptions: %r', e) logger.error('Error occured while getting subscriptions: %r', e)
return return
@ -75,7 +75,7 @@ class NewslettersManageForm(forms.Form):
def save(self): def save(self):
self.full_clean() self.full_clean()
subscriptions = [] subscriptions = []
for key, value in self.cleaned_data.iteritems(): for key, value in self.cleaned_data.items():
subscriptions.append({ subscriptions.append({
'id': key, 'id': key,
'transports': value 'transports': value

View File

@ -16,7 +16,6 @@
import logging import logging
import json import json
import urlparse
from requests.exceptions import RequestException, HTTPError from requests.exceptions import RequestException, HTTPError
@ -70,10 +69,10 @@ class NewslettersCell(CellBase):
return slugify(name.strip()) return slugify(name.strip())
def get_resources_restrictions(self): def get_resources_restrictions(self):
return filter(None, map(self.simplify, self.resources_restrictions.strip().split(','))) return list(filter(None, map(self.simplify, self.resources_restrictions.strip().split(','))))
def get_transports_restrictions(self): def get_transports_restrictions(self):
return filter(None, map(self.simplify, self.transports_restrictions.strip().split(','))) return list(filter(None, map(self.simplify, self.transports_restrictions.strip().split(','))))
def check_resource(self, resource): def check_resource(self, resource):
restrictions = self.get_resources_restrictions() restrictions = self.get_resources_restrictions()
@ -130,7 +129,7 @@ class NewslettersCell(CellBase):
logger.error(u'set subscriptions on %s returned an HTTP error code: %s', logger.error(u'set subscriptions on %s returned an HTTP error code: %s',
response.request.url, response.status_code) response.request.url, response.status_code)
raise SubscriptionsSaveError raise SubscriptionsSaveError
except RequestException, e: except RequestException as e:
logger.error(u'set subscriptions on %s failed with exception: %s', logger.error(u'set subscriptions on %s failed with exception: %s',
endpoint, e) endpoint, e)
raise SubscriptionsSaveError raise SubscriptionsSaveError

View File

@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.encoding import force_text
from rest_framework import serializers, permissions, status from rest_framework import serializers, permissions, status
from rest_framework.generics import GenericAPIView from rest_framework.generics import GenericAPIView
from rest_framework.response import Response from rest_framework.response import Response
@ -55,7 +57,7 @@ class Add(GenericAPIView):
duration=data.get('duration'), duration=data.get('duration'),
) )
except ValueError as e: except ValueError as e:
response = {'err': 1, 'err_desc': {'id': [unicode(e)]}} response = {'err': 1, 'err_desc': {'id': [force_text(e)]}}
return Response(response, status.HTTP_400_BAD_REQUEST) return Response(response, status.HTTP_400_BAD_REQUEST)
else: else:
response = {'err': 0, 'data': {'id': notification.public_id}} response = {'err': 0, 'data': {'id': notification.public_id}}

View File

@ -18,6 +18,7 @@ import re
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now, timedelta from django.utils.timezone import now, timedelta
from django.db.models import Q from django.db.models import Q
@ -61,6 +62,7 @@ class NotificationQuerySet(QuerySet):
end_timestamp=past_end_timestamp, acked=True) end_timestamp=past_end_timestamp, acked=True)
@python_2_unicode_compatible
class Notification(models.Model): class Notification(models.Model):
ID_RE = r'^[\w-]+:[\w-]+$' ID_RE = r'^[\w-]+:[\w-]+$'
@ -82,7 +84,7 @@ class Notification(models.Model):
('user', 'external_id'), ('user', 'external_id'),
) )
def __unicode__(self): def __str__(self):
return self.summary return self.summary
@property @property
@ -136,7 +138,7 @@ class Notification(models.Model):
if id: if id:
try: try:
id = unicode(id) id = force_text(id)
except Exception as e: except Exception as e:
raise ValueError('id must be convertible to unicode', e) raise ValueError('id must be convertible to unicode', e)
if not re.match(cls.ID_RE, id): if not re.match(cls.ID_RE, id):

View File

@ -500,6 +500,6 @@ class TrackingCodeInputCell(CellBase):
def get_cell_extra_context(self, context): def get_cell_extra_context(self, context):
extra_context = super(TrackingCodeInputCell, self).get_cell_extra_context(context) extra_context = super(TrackingCodeInputCell, self).get_cell_extra_context(context)
if not self.wcs_site: if not self.wcs_site:
self.wcs_site = get_wcs_services().keys()[0] self.wcs_site = list(get_wcs_services().keys())[0]
extra_context['url'] = get_wcs_services().get(self.wcs_site).get('url') extra_context['url'] = get_wcs_services().get(self.wcs_site).get('url')
return extra_context return extra_context

View File

@ -1,7 +1,7 @@
{% load i18n combo %} {% load i18n combo %}
{% block cell-content %} {% block cell-content %}
<h2>{% trans 'Current Drafts' %}</h2> <h2>{% trans 'Current Drafts' %}</h2>
{% for slug, forms in current_drafts.iteritems %} {% for slug, forms in current_drafts.items %}
<div class="current-drafts-{{ slug }} current-drafts list-of-forms"> <div class="current-drafts-{{ slug }} current-drafts list-of-forms">
{% if forms.data %} {% if forms.data %}
<ul> <ul>

View File

@ -1,7 +1,7 @@
{% load i18n %} {% load i18n %}
{% block cell-content %} {% block cell-content %}
<h2>{% trans 'Current Forms' %}</h2> <h2>{% trans 'Current Forms' %}</h2>
{% for slug, forms in current_forms.iteritems %} {% for slug, forms in current_forms.items %}
<div class="current-forms-{{ slug }} current-forms list-of-forms"> <div class="current-forms-{{ slug }} current-forms list-of-forms">
{% include "combo/wcs/list_of_forms.html" with forms=forms %} {% include "combo/wcs/list_of_forms.html" with forms=forms %}
</div> </div>

View File

@ -1,7 +1,7 @@
{% load i18n %} {% load i18n %}
{% block cell-content %} {% block cell-content %}
<h2>{% trans 'Form Categories' %}</h2> <h2>{% trans 'Form Categories' %}</h2>
{% for slug, categories in form_categories.iteritems %} {% for slug, categories in form_categories.items %}
<div class="categories-{{ slug }}"> <div class="categories-{{ slug }}">
<h3>{{ categories.title }}</h3> <h3>{{ categories.title }}</h3>
<ul> <ul>

View File

@ -1,7 +1,7 @@
{% load combo i18n %} {% load combo i18n %}
{% block cell-content %} {% block cell-content %}
<h2>{% trans 'All Forms' %}</h2> <h2>{% trans 'All Forms' %}</h2>
{% for slug, forms in user_forms.iteritems %} {% for slug, forms in user_forms.items %}
<div class="user-forms-{{ slug }} user-all-forms list-of-forms"> <div class="user-forms-{{ slug }} user-all-forms list-of-forms">
{% include "combo/wcs/list_of_forms.html" with forms=forms %} {% include "combo/wcs/list_of_forms.html" with forms=forms %}
</div> </div>

View File

@ -1,7 +1,7 @@
{% load combo i18n %} {% load combo i18n %}
{% block cell-content %} {% block cell-content %}
<h2>{% trans 'Done Forms' %}</h2> <h2>{% trans 'Done Forms' %}</h2>
{% for slug, forms in user_forms.iteritems %} {% for slug, forms in user_forms.items %}
<div class="user-forms-{{ slug }} list-of-forms"> <div class="user-forms-{{ slug }} list-of-forms">
{% include "combo/wcs/list_of_forms.html" with forms=forms %} {% include "combo/wcs/list_of_forms.html" with forms=forms %}
</div> </div>

View File

@ -32,7 +32,7 @@ def get_wcs_json(wcs_site, path):
def get_wcs_options(url, include_category_slug=False): def get_wcs_options(url, include_category_slug=False):
references = [] references = []
for wcs_key, wcs_site in get_wcs_services().iteritems(): for wcs_key, wcs_site in get_wcs_services().items():
site_title = wcs_site.get('title') site_title = wcs_site.get('title')
response_json = get_wcs_json(wcs_site, url) response_json = get_wcs_json(wcs_site, url)
if type(response_json) is dict: if type(response_json) is dict:
@ -51,5 +51,5 @@ def get_wcs_options(url, include_category_slug=False):
else: else:
reference = '%s:%s' % (wcs_key, slug) reference = '%s:%s' % (wcs_key, slug)
references.append((reference, label)) references.append((reference, label))
references.sort(lambda x, y: cmp(x[1], y[1])) references.sort(key=lambda x: x[1])
return references return references

View File

@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import urlparse
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponseRedirect, HttpResponseBadRequest from django.http import HttpResponseRedirect, HttpResponseBadRequest
from django.http import HttpResponseRedirect
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View from django.views.generic import View

View File

@ -18,6 +18,7 @@ import json
import sys import sys
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils.encoding import force_text
from combo.data.utils import import_site, MissingGroups from combo.data.utils import import_site, MissingGroups
@ -44,4 +45,4 @@ class Command(BaseCommand):
if_empty=options['if_empty'], if_empty=options['if_empty'],
clean=options['clean']) clean=options['clean'])
except MissingGroups as e: except MissingGroups as e:
print >> sys.stderr, unicode(e) sys.stderr.write(force_text(e) + '\n')

View File

@ -24,7 +24,6 @@ import os
import re import re
import requests import requests
import subprocess import subprocess
import urlparse
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
@ -40,9 +39,11 @@ from django.dispatch import receiver
from django.forms import models as model_forms from django.forms import models as model_forms
from django import forms from django import forms
from django import template from django import template
from django.utils.encoding import force_text from django.utils import six
from django.utils.encoding import python_2_unicode_compatible, force_text, smart_bytes
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.forms.widgets import MediaDefiningClass from django.forms.widgets import MediaDefiningClass
@ -116,6 +117,7 @@ class PageManager(models.Manager):
return queryset.filter(snapshot__isnull=True) return queryset.filter(snapshot__isnull=True)
@python_2_unicode_compatible
class Page(models.Model): class Page(models.Model):
objects = PageManager() objects = PageManager()
snapshots = PageManager(snapshots=True) snapshots = PageManager(snapshots=True)
@ -149,7 +151,7 @@ class Page(models.Model):
class Meta: class Meta:
ordering = ['order'] ordering = ['order']
def __unicode__(self): def __str__(self):
return self.title return self.title
def natural_key(self): def natural_key(self):
@ -349,7 +351,7 @@ class Page(models.Model):
del cell['pk'] del cell['pk']
del cell['fields']['page'] del cell['fields']['page']
cell['fields']['groups'] = [x[0] for x in cell['fields']['groups']] cell['fields']['groups'] = [x[0] for x in cell['fields']['groups']]
for key in cell['fields'].keys(): for key in list(cell['fields'].keys()):
if key.startswith('cached_'): if key.startswith('cached_'):
del cell['fields'][key] del cell['fields'][key]
return serialized_page return serialized_page
@ -357,14 +359,14 @@ class Page(models.Model):
@classmethod @classmethod
def load_serialized_page(cls, json_page, snapshot=None): def load_serialized_page(cls, json_page, snapshot=None):
json_page['model'] = 'data.page' json_page['model'] = 'data.page'
json_page['fields']['groups'] = [[x] for x in json_page['fields']['groups'] if isinstance(x, basestring)] json_page['fields']['groups'] = [[x] for x in json_page['fields']['groups'] if isinstance(x, six.string_types)]
page, created = Page.objects.get_or_create(slug=json_page['fields']['slug'], snapshot=snapshot) page, created = Page.objects.get_or_create(slug=json_page['fields']['slug'], snapshot=snapshot)
json_page['pk'] = page.id json_page['pk'] = page.id
page = [x for x in serializers.deserialize('json', json.dumps([json_page]))][0] page = [x for x in serializers.deserialize('json', json.dumps([json_page]))][0]
page.object.snapshot = snapshot page.object.snapshot = snapshot
page.save() page.save()
for cell in json_page.get('cells'): for cell in json_page.get('cells'):
cell['fields']['groups'] = [[x] for x in cell['fields']['groups'] if isinstance(x, basestring)] cell['fields']['groups'] = [[x] for x in cell['fields']['groups'] if isinstance(x, six.string_types)]
if snapshot: if snapshot:
cell['fields']['page'] = page.object.id cell['fields']['page'] = page.object.id
else: else:
@ -462,8 +464,8 @@ class CellMeta(MediaDefiningClass, ModelBase):
pass pass
class CellBase(models.Model): @python_2_unicode_compatible
__metaclass__ = CellMeta class CellBase(six.with_metaclass(CellMeta, models.Model)):
page = models.ForeignKey(Page) page = models.ForeignKey(Page)
placeholder = models.CharField(max_length=20) placeholder = models.CharField(max_length=20)
@ -499,13 +501,13 @@ class CellBase(models.Model):
class Meta: class Meta:
abstract = True abstract = True
def __unicode__(self): def __str__(self):
label = unicode(self.get_verbose_name()) label = self.get_verbose_name()
additional_label = self.get_additional_label() additional_label = self.get_additional_label()
if label and additional_label: if label and additional_label:
return '%s (%s)' % (label, re.sub(r'\r?\n', ' ', force_text(additional_label))) return u'%s (%s)' % (label, re.sub(r'\r?\n', u' ', force_text(additional_label)))
else: else:
return label return force_text(label)
@classmethod @classmethod
def get_verbose_name(cls): def get_verbose_name(cls):
@ -561,7 +563,7 @@ class CellBase(models.Model):
if cell_filter and not cell_filter(klass): if cell_filter and not cell_filter(klass):
continue continue
cells.extend(klass.objects.filter(**kwargs)) cells.extend(klass.objects.filter(**kwargs))
cells.sort(lambda x, y: cmp(x.order, y.order)) cells.sort(key=lambda x: x.order)
return cells return cells
def get_reference(self): def get_reference(self):
@ -699,7 +701,7 @@ class CellBase(models.Model):
} }
if not self.is_relevant(context): if not self.is_relevant(context):
return '' return ''
from HTMLParser import HTMLParser from django.utils.six.moves.html_parser import HTMLParser
return HTMLParser().unescape(strip_tags(self.render(context))) return HTMLParser().unescape(strip_tags(self.render(context)))
def get_external_links_data(self): def get_external_links_data(self):
@ -832,7 +834,7 @@ class LinkCell(CellBase):
return context return context
def get_default_form_class(self): def get_default_form_class(self):
from forms import LinkCellForm from .forms import LinkCellForm
return LinkCellForm return LinkCellForm
def render_for_search(self): def render_for_search(self):
@ -867,7 +869,7 @@ class FeedCell(CellBase):
if context.get('placeholder_search_mode'): if context.get('placeholder_search_mode'):
# don't call webservices when we're just looking for placeholders # don't call webservices when we're just looking for placeholders
return extra_context return extra_context
cache_key = hashlib.md5(self.url).hexdigest() cache_key = hashlib.md5(smart_bytes(self.url)).hexdigest()
feed_content = cache.get(cache_key) feed_content = cache.get(cache_key)
if not feed_content: if not feed_content:
feed_response = requests.get(utils.get_templated_url(self.url)) feed_response = requests.get(utils.get_templated_url(self.url))
@ -886,7 +888,7 @@ class FeedCell(CellBase):
return extra_context return extra_context
def render(self, context): def render(self, context):
cache_key = hashlib.md5(self.url).hexdigest() cache_key = hashlib.md5(smart_bytes(self.url)).hexdigest()
feed_content = cache.get(cache_key) feed_content = cache.get(cache_key)
if not context.get('synchronous') and feed_content is None: if not context.get('synchronous') and feed_content is None:
raise NothingInCacheException() raise NothingInCacheException()
@ -1091,13 +1093,13 @@ class JsonCellBase(CellBase):
) )
except requests.RequestException as e: except requests.RequestException as e:
extra_context[data_key + '_status'] = -1 extra_context[data_key + '_status'] = -1
extra_context[data_key + '_error'] = unicode(e) extra_context[data_key + '_error'] = force_text(e)
extra_context[data_key + '_exception'] = e extra_context[data_key + '_exception'] = e
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
if log_errors: if log_errors:
logger.warning(u'error on request %r: %s', url, unicode(e)) logger.warning(u'error on request %r: %s', url, force_text(e))
else: else:
logger.debug(u'error on request %r: %s', url, unicode(e)) logger.debug(u'error on request %r: %s', url, force_text(e))
continue continue
extra_context[data_key + '_status'] = json_response.status_code extra_context[data_key + '_status'] = json_response.status_code
if json_response.status_code // 100 == 2: if json_response.status_code // 100 == 2:
@ -1243,7 +1245,7 @@ class ConfigJsonCell(JsonCellBase):
'group': _('Extra'), 'group': _('Extra'),
'cell_type_str': cls.get_cell_type_str(), 'cell_type_str': cls.get_cell_type_str(),
}) })
l.sort(lambda x, y: cmp(x.get('name'), y.get('name'))) l.sort(key=lambda x: x.get('name'))
return l return l
def get_label(self): def get_label(self):

View File

@ -16,6 +16,8 @@
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.db import transaction from django.db import transaction
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from combo.apps.assets.models import Asset from combo.apps.assets.models import Asset
@ -23,11 +25,12 @@ from combo.apps.maps.models import MapLayer
from .models import Page from .models import Page
@python_2_unicode_compatible
class MissingGroups(Exception): class MissingGroups(Exception):
def __init__(self, names): def __init__(self, names):
self.names = names self.names = names
def __unicode__(self): def __str__(self):
return _('Missing groups: %s') % ', '.join(self.names) return _('Missing groups: %s') % ', '.join(self.names)
@ -51,10 +54,10 @@ def import_site(data, if_empty=False, clean=False):
groups = set() groups = set()
for page in data.get('pages') or []: for page in data.get('pages') or []:
for group in page['fields']['groups']: for group in page['fields']['groups']:
groups.add(group if isinstance(group, basestring) else group[0]) groups.add(group if isinstance(group, six.string_types) else group[0])
for cell in page['cells']: for cell in page['cells']:
for group in cell['fields']['groups']: for group in cell['fields']['groups']:
groups.add(group if isinstance(group, basestring) else group[0]) groups.add(group if isinstance(group, six.string_types) else group[0])
existing_groups = set([x.name for x in Group.objects.filter(name__in=groups)]) existing_groups = set([x.name for x in Group.objects.filter(name__in=groups)])
missing_groups = groups - existing_groups missing_groups = groups - existing_groups

View File

@ -69,7 +69,7 @@ class PageSelectTemplateForm(forms.ModelForm):
super(PageSelectTemplateForm, self).__init__(*args, **kwargs) super(PageSelectTemplateForm, self).__init__(*args, **kwargs)
templates = [(x[0], x[1]['name']) for x in settings.COMBO_PUBLIC_TEMPLATES.items()] templates = [(x[0], x[1]['name']) for x in settings.COMBO_PUBLIC_TEMPLATES.items()]
templates = [x for x in templates if self.template_exists(x[0])] templates = [x for x in templates if self.template_exists(x[0])]
templates.sort(lambda x, y: cmp(x[1], y[1])) templates.sort(key=lambda x: x[1])
if 'template_name' in self.fields: if 'template_name' in self.fields:
self.fields['template_name'].widget = forms.Select(choices=templates) self.fields['template_name'].widget = forms.Select(choices=templates)

View File

@ -237,7 +237,7 @@ class PageView(DetailView):
if 'data' in cell_type_groups.keys(): if 'data' in cell_type_groups.keys():
cell_type_groups[''] = cell_type_groups.get('data') cell_type_groups[''] = cell_type_groups.get('data')
del cell_type_groups['data'] del cell_type_groups['data']
context['cell_type_groups'] = cell_type_groups.items() context['cell_type_groups'] = list(cell_type_groups.items())
context['cell_type_groups'].sort(key=lambda x: x[0]) context['cell_type_groups'].sort(key=lambda x: x[0])
cells = CellBase.get_cells(page_id=self.object.id) cells = CellBase.get_cells(page_id=self.object.id)

View File

@ -220,3 +220,7 @@ def get_group(group_list, group_name):
@register.filter(name='is_empty_placeholder') @register.filter(name='is_empty_placeholder')
def is_empty_placeholder(page, placeholder_name): def is_empty_placeholder(page, placeholder_name):
return len([x for x in page.get_cells() if x.placeholder == placeholder_name]) == 0 return len([x for x in page.get_cells() if x.placeholder == placeholder_name]) == 0
@register.filter(name='list')
def as_list(obj):
return list(obj)

View File

@ -15,8 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import json import json
import urllib
import urlparse
import django import django
from django.conf import settings from django.conf import settings
@ -31,6 +29,9 @@ from django.shortcuts import render, resolve_url
from django.template import engines from django.template import engines
from django.template.loader import get_template, TemplateDoesNotExist from django.template.loader import get_template, TemplateDoesNotExist
from django.utils import lorem_ipsum, timezone from django.utils import lorem_ipsum, timezone
from django.utils.encoding import force_text
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.six.moves.urllib import parse as urllib
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -102,14 +103,14 @@ def ajax_page_cell(request, page_pk, cell_reference):
except PostException as e: except PostException as e:
exception = e exception = e
if not request.is_ajax(): if not request.is_ajax():
messages.error(request, e.message or _('Error sending data.')) messages.error(request, force_text(e) if force_text(e) != 'None' else _('Error sending data.'))
if not request.is_ajax(): if not request.is_ajax():
return HttpResponseRedirect(cell.page.get_online_url()) return HttpResponseRedirect(cell.page.get_online_url())
response = render_cell(request, cell) response = render_cell(request, cell)
if exception: if exception:
response['x-error-message'] = exception.message response['x-error-message'] = force_text(exception)
return response return response
def render_cell(request, cell): def render_cell(request, cell):

View File

@ -315,4 +315,4 @@ NEWSLETTERS_CELL_ENABLED = False
local_settings_file = os.environ.get('COMBO_SETTINGS_FILE', local_settings_file = os.environ.get('COMBO_SETTINGS_FILE',
os.path.join(os.path.dirname(__file__), 'local_settings.py')) os.path.join(os.path.dirname(__file__), 'local_settings.py'))
if os.path.exists(local_settings_file): if os.path.exists(local_settings_file):
execfile(local_settings_file) exec(open(local_settings_file).read())

View File

@ -20,6 +20,9 @@ from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2 from Crypto.Protocol.KDF import PBKDF2
from Crypto import Random from Crypto import Random
from django.utils import six
from django.utils.encoding import force_text
class DecryptionError(Exception): class DecryptionError(Exception):
pass pass
@ -33,7 +36,7 @@ def aes_hex_encrypt(key, data):
aes_key = PBKDF2(key, iv) aes_key = PBKDF2(key, iv)
aes = AES.new(aes_key, AES.MODE_CFB, iv) aes = AES.new(aes_key, AES.MODE_CFB, iv)
crypted = aes.encrypt(data) crypted = aes.encrypt(data)
return '%s%s' % (binascii.hexlify(iv[:2]), binascii.hexlify(crypted)) return force_text(b'%s%s' % (binascii.hexlify(iv[:2]), binascii.hexlify(crypted)))
def aes_hex_decrypt(key, payload, raise_on_error=True): def aes_hex_decrypt(key, payload, raise_on_error=True):
'''Decrypt data encrypted with aes_base64_encrypt''' '''Decrypt data encrypted with aes_base64_encrypt'''
@ -46,10 +49,10 @@ def aes_hex_decrypt(key, payload, raise_on_error=True):
try: try:
iv = binascii.unhexlify(iv) * 8 iv = binascii.unhexlify(iv) * 8
crypted = binascii.unhexlify(crypted) crypted = binascii.unhexlify(crypted)
except TypeError: except (TypeError, binascii.Error):
if raise_on_error: if raise_on_error:
raise DecryptionError('incorrect hexadecimal encoding') raise DecryptionError('incorrect hexadecimal encoding')
return None return None
aes_key = PBKDF2(key, iv) aes_key = PBKDF2(key, iv)
aes = AES.new(aes_key, AES.MODE_CFB, iv) aes = AES.new(aes_key, AES.MODE_CFB, iv)
return aes.decrypt(crypted) return force_text(aes.decrypt(crypted), 'utf-8')

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from HTMLParser import HTMLParser from django.utils.six.moves.html_parser import HTMLParser
from django.template.context import BaseContext from django.template.context import BaseContext
from django.utils.html import strip_tags from django.utils.html import strip_tags

View File

@ -16,8 +16,6 @@
import hashlib import hashlib
import logging import logging
from StringIO import StringIO
import urlparse
from requests import Response, Session as RequestsSession from requests import Response, Session as RequestsSession
@ -25,6 +23,8 @@ from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.utils.encoding import smart_bytes from django.utils.encoding import smart_bytes
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.six import BytesIO
from .signature import sign_url from .signature import sign_url
@ -110,7 +110,7 @@ class Requests(RequestsSession):
if cache_content and not invalidate_cache: if cache_content and not invalidate_cache:
response = Response() response = Response()
response.status_code = 200 response.status_code = 200
response.raw = StringIO(cache_content) response.raw = BytesIO(smart_bytes(cache_content))
return response return response
elif raise_if_not_cached: elif raise_if_not_cached:
raise NothingInCacheException() raise NothingInCacheException()

View File

@ -19,10 +19,13 @@ import datetime
import hmac import hmac
import hashlib import hashlib
import random import random
import urlparse
from django.conf import settings from django.conf import settings
from django.utils.encoding import smart_bytes
from django.utils.http import quote, urlencode from django.utils.http import quote, urlencode
from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse
# Simple signature scheme for query strings # Simple signature scheme for query strings
@ -50,7 +53,7 @@ def sign_query(query, key, algo='sha256', timestamp=None, nonce=None):
def sign_string(s, key, algo='sha256', timedelta=30): def sign_string(s, key, algo='sha256', timedelta=30):
digestmod = getattr(hashlib, algo) digestmod = getattr(hashlib, algo)
hash = hmac.HMAC(str(key), digestmod=digestmod, msg=s) hash = hmac.HMAC(smart_bytes(key), digestmod=digestmod, msg=smart_bytes(s))
return hash.digest() return hash.digest()
def check_request_signature(django_request, keys=[]): def check_request_signature(django_request, keys=[]):
@ -60,8 +63,8 @@ def check_request_signature(django_request, keys=[]):
orig = django_request.GET.get('orig', '') orig = django_request.GET.get('orig', '')
known_services = getattr(settings, 'KNOWN_SERVICES', None) known_services = getattr(settings, 'KNOWN_SERVICES', None)
if known_services and orig: if known_services and orig:
for services in known_services.itervalues(): for services in known_services.values():
for service in services.itervalues(): for service in services.values():
if 'verif_orig' in service and service['verif_orig'] == orig: if 'verif_orig' in service and service['verif_orig'] == orig:
keys.append(service['secret']) keys.append(service['secret'])
break break
@ -95,8 +98,12 @@ def check_string(s, signature, keys, algo='sha256'):
continue continue
res = 0 res = 0
# constant time compare # constant time compare
for a, b in zip(signature, signature2): if six.PY3:
res |= ord(a) ^ ord(b) for a, b in zip(signature, signature2):
res |= a ^ b
else:
for a, b in zip(signature, signature2):
res |= ord(a) ^ ord(b)
if res == 0: if res == 0:
return True return True
return False return False

View File

@ -17,6 +17,7 @@
import re import re
from django.conf import settings from django.conf import settings
from django.utils.encoding import force_text
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
from django.utils.http import quote from django.utils.http import quote
@ -58,5 +59,5 @@ def get_templated_url(url, context=None):
return '[' return '['
if varname not in template_vars: if varname not in template_vars:
raise TemplateError('unknown variable %s', varname) raise TemplateError('unknown variable %s', varname)
return unicode(template_vars[varname]) return force_text(template_vars[varname])
return re.sub(r'(\[.+?\])', repl, url) return re.sub(r'(\[.+?\])', repl, url)

View File

@ -36,7 +36,7 @@ def get_version():
p = subprocess.Popen(['git', 'describe', '--dirty', '--match=v*'], stdout=subprocess.PIPE) p = subprocess.Popen(['git', 'describe', '--dirty', '--match=v*'], stdout=subprocess.PIPE)
result = p.communicate()[0] result = p.communicate()[0]
if p.returncode == 0: if p.returncode == 0:
version = result.split()[0][1:] version = str(result.split()[0][1:])
version = version.replace('-', '.') version = version.replace('-', '.')
return version return version
return '0' return '0'

View File

@ -1,12 +1,12 @@
[tox] [tox]
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/combo/{env:BRANCH_NAME:} toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/combo/{env:BRANCH_NAME:}
envlist = django{18,111} envlist = py2-django18,coverage-py2-django111-pylint,py3-django111
[testenv] [testenv]
usedevelop = True usedevelop = True
basepython = python2 basepython = python2
setenv = setenv =
WCSCTL=wcs/wcsctl.py py2: WCSCTL=wcs/wcsctl.py
DJANGO_SETTINGS_MODULE=combo.settings DJANGO_SETTINGS_MODULE=combo.settings
COMBO_SETTINGS_FILE=tests/settings.py COMBO_SETTINGS_FILE=tests/settings.py
deps = deps =
@ -22,10 +22,10 @@ deps =
pylint<1.8 pylint<1.8
pylint-django<0.9 pylint-django<0.9
django-webtest<1.9.3 django-webtest<1.9.3
quixote<3.0
vobject
psycopg2 psycopg2
django-mellon django-mellon
py2: quixote<3.0
py2: vobject
commands = commands =
./getlasso.sh ./getlasso.sh
python manage.py compilemessages python manage.py compilemessages