diff --git a/example/models.py b/example/models.py index eab1166..b40e8d9 100644 --- a/example/models.py +++ b/example/models.py @@ -6,5 +6,8 @@ from questionnaire.models import Landing class Book(models.Model): title = models.CharField(max_length=1000, default="") landings = GenericRelation(Landing, related_query_name='items') + def __unicode__(self): return self.title + + __str__ = __unicode__ diff --git a/questionnaire/legacy_tests.py b/questionnaire/legacy_tests.py index ac5b5eb..d1db78e 100644 --- a/questionnaire/legacy_tests.py +++ b/questionnaire/legacy_tests.py @@ -86,7 +86,7 @@ class TypeTest(TestCase): response = c.post('/q/test:test/1/', ansdict) self.assertEqual(response.status_code, 200) errors = response.context[-1]['errors'] - self.assertEqual(len(errors), 1) and errors.has_key('3') + self.assertEqual(len(errors), 1) and '3' in errors def test050_missing_question(self): diff --git a/questionnaire/models.py b/questionnaire/models.py index 5140cfd..d90a52e 100644 --- a/questionnaire/models.py +++ b/questionnaire/models.py @@ -3,6 +3,7 @@ import json import re import uuid from datetime import datetime +from six import text_type as unicodestr from transmeta import TransMeta from django.conf import settings @@ -58,6 +59,12 @@ class Subject(models.Model): else: return u'%s, %s (%s)' % (self.surname, self.givenname, self.email) + __str__ = __unicode__ + + + def __str__(self): + return self.__unicode() + def next_runid(self): "Return the string form of the runid for the upcoming run" return str(self.nextrun.year) @@ -94,6 +101,8 @@ class Questionnaire(models.Model): def __unicode__(self): return self.name + __str__ = __unicode__ + def questionsets(self): if not hasattr(self, "__qscache"): self.__qscache = \ @@ -151,6 +160,8 @@ class DBStylesheet(models.Model): def __unicode__(self): return self.inclusion_tag + __str__ = __unicode__ + class QuestionSet(models.Model): __metaclass__ = TransMeta @@ -216,6 +227,8 @@ class QuestionSet(models.Model): def __unicode__(self): return u'%s: %s' % (self.questionnaire.name, self.heading) + __str__ = __unicode__ + class Meta: translate = ('text',) index_together = [ @@ -281,7 +294,7 @@ class RunInfo(models.Model): "runinfo.set_cookie(key, value). If value is None, delete cookie" key = key.lower().strip() cookies = self.get_cookiedict() - if type(value) not in (int, float, str, unicode, type(None)): + if type(value) not in (int, float, unicodestr, type(None)): raise Exception("Can only store cookies of type integer or string") if value is None: if key in cookies: @@ -308,8 +321,12 @@ class RunInfo(models.Model): self.__cookiecache = json.loads(self.cookies) return self.__cookiecache - def __unicode__(self): - return "%s: %s, %s" % (self.run.runid, self.subject.surname, self.subject.givenname) + __str__ = __unicode__ + + + def __str__(self): + return self.__unicode() + class Meta: verbose_name_plural = 'Run Info' @@ -332,8 +349,11 @@ class RunInfoHistory(models.Model): ) questionnaire = models.ForeignKey(Questionnaire, on_delete=models.CASCADE) - def __unicode__(self): - return "%s: %s on %s" % (self.run.runid, self.subject, self.completed) + __str__ = __unicode__ + + def __str__(self): + return self.__unicode() + def answers(self): "Returns the query for the answers." @@ -392,7 +412,10 @@ class Question(models.Model): return d def __unicode__(self): - return u'{%s} (%s) %s' % (unicode(self.questionset), self.number, self.text) + return u'{%s} (%s) %s' % (unicodestr(self.questionset), self.number, self.text) + + __str__ = __unicode__ + def sameas(self): if self.type == 'sameas': @@ -480,6 +503,9 @@ class Choice(models.Model): def __unicode__(self): return u'(%s) %d. %s' % (self.question.number, self.sortid, self.text) + __str__ = __unicode__ + + class Meta: translate = ('text',) index_together = [ @@ -495,6 +521,8 @@ class Answer(models.Model): def __unicode__(self): return "Answer(%s: %s, %s)" % (self.question.number, self.subject.surname, self.subject.givenname) + __str__ = __unicode__ + def split_answer(self): """ Decode stored answer value and return as a list of choices. @@ -535,7 +563,7 @@ class Answer(models.Model): runinfo.remove_tags(tags) for split_answer in self.split_answer(): - if unicode(split_answer) == choice.value: + if unicodestr(split_answer) == choice.value: tags_to_add.extend(tags) runinfo.add_tags(tags_to_add) diff --git a/questionnaire/page/models.py b/questionnaire/page/models.py index 5452b90..725e5c9 100644 --- a/questionnaire/page/models.py +++ b/questionnaire/page/models.py @@ -13,6 +13,8 @@ class Page(models.Model): def __unicode__(self): return u"Page[%s]" % self.slug + __str__ = __unicode__ + def get_absolute_url(self): return reverse('questionnaire.page.views.page', kwargs={'page_to_render':self.slug}) diff --git a/questionnaire/qprocessors/simple.py b/questionnaire/qprocessors/simple.py index 58558dd..47f0647 100644 --- a/questionnaire/qprocessors/simple.py +++ b/questionnaire/qprocessors/simple.py @@ -108,7 +108,7 @@ def process_simple(question, ansdict): answords = len(ans.split()) if answords > maxwords: raise AnswerException(_(u'Answer is ' + str(answords) + ' words. Please shorten answer to ' + str(maxwords) + ' words or less')) - if ansdict.has_key('comment') and len(ansdict['comment']) > 0: + if 'comment' in ansdict and len(ansdict['comment']) > 0: return dumps([ans, [ansdict['comment']]]) if ans: return dumps([ans]) diff --git a/questionnaire/qprocessors/timeperiod.py b/questionnaire/qprocessors/timeperiod.py index 001614f..ff22316 100644 --- a/questionnaire/qprocessors/timeperiod.py +++ b/questionnaire/qprocessors/timeperiod.py @@ -1,3 +1,5 @@ +from six import text_type as unicodestr + from django.utils.translation import ugettext as _, ugettext_lazy from .. import add_type, question_proc, answer_proc, AnswerException @@ -29,7 +31,7 @@ def question_timeperiod(request, question): for x in units: if x in perioddict: - timeperiods.append( (x, unicode(perioddict[x]), unitselected==x) ) + timeperiods.append( (x, unicodestr(perioddict[x]), unitselected==x) ) return { "required" : "required" in cd, "timeperiods" : timeperiods, @@ -38,7 +40,7 @@ def question_timeperiod(request, question): @answer_proc('timeperiod') def process_timeperiod(question, answer): - if not answer['ANSWER'] or not answer.has_key('unit'): + if not answer['ANSWER'] or not 'unit in 'answer: raise AnswerException(_(u"Invalid time period")) period = answer['ANSWER'].strip() if period: diff --git a/questionnaire/templatetags/landings.py b/questionnaire/templatetags/landings.py index 4beb4a1..a35e2f6 100644 --- a/questionnaire/templatetags/landings.py +++ b/questionnaire/templatetags/landings.py @@ -4,7 +4,7 @@ register = django.template.Library() @register.simple_tag(takes_context=True) def render_with_landing(context, text): - if not context.has_key('landing_object') and context.has_key('runinfo'): + if not 'landing_object' in context.has_key and 'runinfo' in context: landing = context['runinfo'].landing context['landing_object'] = landing.content_object if landing else '' if text: diff --git a/questionnaire/utils.py b/questionnaire/utils.py index fabb9b2..d058218 100644 --- a/questionnaire/utils.py +++ b/questionnaire/utils.py @@ -2,6 +2,7 @@ import codecs import cStringIO import csv +from six import text_type as unicodestr from django.conf import settings try: @@ -76,7 +77,7 @@ class UnicodeWriter: self.encoder = codecs.getincrementalencoder(encoding)() def writerow(self, row): - self.writer.writerow([unicode(s).encode("utf-8") for s in row]) + self.writer.writerow([unicodestr(s).encode("utf-8") for s in row]) # Fetch UTF-8 output from the queue ... data = self.queue.getvalue() data = data.decode("utf-8") diff --git a/questionnaire/views.py b/questionnaire/views.py index 002adf7..6e790ef 100644 --- a/questionnaire/views.py +++ b/questionnaire/views.py @@ -2,6 +2,7 @@ # vim: set fileencoding=utf-8 import json import logging +from six import text_type as unicodestr import tempfile from compat import commit_on_success, commit, rollback @@ -852,7 +853,7 @@ def answer_export(questionnaire, answers=None, answer_filter=None): choice = choice[0] col = coldict.get(qnum + '-freeform', None) if col is None: # look for enumerated choice column (multiple-choice) - col = coldict.get(qnum + '-' + unicode(choice), None) + col = coldict.get(qnum + '-' + unicodestr(choice), None) if col is None: # single-choice items if ((not qchoicedict[answer.question.id]) or choice in qchoicedict[answer.question.id]): diff --git a/setup.py b/setup.py index 57013ad..db5c98b 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def read(fname): setup( name="fef-questionnaire", - version="4.0.1", + version="5.0", description="A Django application for creating online questionnaires/surveys.", long_description=read("README.md"), author="Eldest Daughter, LLC., Free Ebook Foundation", @@ -24,6 +24,7 @@ setup( "Operating System :: OS Independent", "Programming Language :: Python", 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.6', "Framework :: Django", ], zip_safe=False, @@ -33,6 +34,7 @@ setup( 'django-compat', 'pyyaml', 'pyparsing' + 'six' ], setup_requires=[ 'versiontools >= 1.6',