From ef02874243be6e4a2a979cf04e7fc5663a419d56 Mon Sep 17 00:00:00 2001 From: Emmanuel Cazenave Date: Fri, 12 Jan 2018 12:13:05 +0100 Subject: [PATCH] django 1.11 support Breaks backward compatibility : now nothing is importable directly from the root module (because django 1.11 requires almost empty root __init__.py for application) Bump version to 2.0.0 Use tox and pytest to run tests. --- django_journal/__init__.py | 76 +--------------------------------------------- django_journal/actions.py | 6 ++-- django_journal/main.py | 72 +++++++++++++++++++++++++++++++++++++++++++ django_journal/models.py | 4 +-- django_journal/tests.py | 28 ++++++++--------- setup.py | 4 +-- tox.ini | 10 ++++++ 7 files changed, 103 insertions(+), 97 deletions(-) create mode 100644 django_journal/main.py create mode 100644 tox.ini diff --git a/django_journal/__init__.py b/django_journal/__init__.py index 5859cbd..afced14 100644 --- a/django_journal/__init__.py +++ b/django_journal/__init__.py @@ -1,75 +1 @@ -import logging - -from exceptions import JournalException -from models import (Journal, Tag, Template) - -import django.db.models -from django.conf import settings - -from decorator import atomic - -__all__ = ('record', 'error_record', 'Journal') -__version__ = '1.25.1' - -def unicode_truncate(s, length, encoding='utf-8'): - '''Truncate an unicode string so that its UTF-8 encoding is less than - length.''' - encoded = s.encode(encoding)[:length] - return encoded.decode(encoding, 'ignore') - -@atomic -def record(tag, template, using=None, **kwargs): - '''Record an event in the journal. The modification is done inside the - current transaction. - - tag: - a string identifier giving the type of the event - tpl: - a format string to describe the event - kwargs: - a mapping of object or data to interpolate in the format string - ''' - template = unicode(template) - tag = Tag.objects.using(using).get_cached(name=tag) - template = Template.objects.using(using).get_cached(content=template) - try: - message = template.content.format(**kwargs) - except (KeyError, IndexError), e: - raise JournalException( - 'Missing variable for the template message', template, e) - try: - logger = logging.getLogger('django.journal.%s' % tag) - if tag.name == 'error' or tag.name.startswith('error-'): - logger.error(message) - elif tag.name == 'warning' or tag.name.startswith('warning-'): - logger.warning(message) - else: - logger.info(message) - except: - try: - logging.getLogger('django.journal').exception('Unable to log msg') - except: - pass # we tried, really, we tried - journal = Journal.objects.using(using).create(tag=tag, template=template, - message=unicode_truncate(message, 128)) - for name, value in kwargs.iteritems(): - if value is None: - continue - tag = Tag.objects.using(using).get_cached(name=name) - if isinstance(value, django.db.models.Model): - journal.objectdata_set.create(tag=tag, content_object=value) - else: - journal.stringdata_set.create(tag=tag, content=unicode(value)) - return journal - -def error_record(tag, tpl, **kwargs): - '''Records error events. - - You must use this function when logging error events. It uses another - database alias than the default one to be immune to transaction rollback - when logging in the middle of a transaction which is going to - rollback. - ''' - return record(tag, tpl, - using=getattr(settings, 'JOURNAL_DB_FOR_ERROR_ALIAS', 'default'), - **kwargs) +__version__ = '2.0.0' diff --git a/django_journal/actions.py b/django_journal/actions.py index a08852a..244e239 100644 --- a/django_journal/actions.py +++ b/django_journal/actions.py @@ -6,14 +6,14 @@ from django.http import HttpResponse from django.core import exceptions -from . import models +from .models import Tag def export_as_csv_generator(queryset): header = ['time', 'tag', 'message'] - tags = set(models.Tag.objects.filter(objectdata__journal__in=queryset).values_list('name', flat=True)) + tags = set(Tag.objects.filter(objectdata__journal__in=queryset).values_list('name', flat=True)) for tag in list(tags): tags.add('%s__id' % tag) - tags |= set(models.Tag.objects.filter(stringdata__journal__in=queryset).values_list('name', flat=True)) + tags |= set(Tag.objects.filter(stringdata__journal__in=queryset).values_list('name', flat=True)) extra_headers = map(lambda s: s.encode('utf-8'), sorted(tags)) yield header+extra_headers for journal in queryset: diff --git a/django_journal/main.py b/django_journal/main.py new file mode 100644 index 0000000..1a43bb8 --- /dev/null +++ b/django_journal/main.py @@ -0,0 +1,72 @@ +import logging + +from django.conf import settings +import django.db.models + +from .decorator import atomic +from .exceptions import JournalException +from .models import (Journal, Tag, Template) + + +def unicode_truncate(s, length, encoding='utf-8'): + '''Truncate an unicode string so that its UTF-8 encoding is less than + length.''' + encoded = s.encode(encoding)[:length] + return encoded.decode(encoding, 'ignore') + +@atomic +def record(tag, template, using=None, **kwargs): + '''Record an event in the journal. The modification is done inside the + current transaction. + + tag: + a string identifier giving the type of the event + tpl: + a format string to describe the event + kwargs: + a mapping of object or data to interpolate in the format string + ''' + template = unicode(template) + tag = Tag.objects.using(using).get_cached(name=tag) + template = Template.objects.using(using).get_cached(content=template) + try: + message = template.content.format(**kwargs) + except (KeyError, IndexError), e: + raise JournalException( + 'Missing variable for the template message', template, e) + try: + logger = logging.getLogger('django.journal.%s' % tag) + if tag.name == 'error' or tag.name.startswith('error-'): + logger.error(message) + elif tag.name == 'warning' or tag.name.startswith('warning-'): + logger.warning(message) + else: + logger.info(message) + except: + try: + logging.getLogger('django.journal').exception('Unable to log msg') + except: + pass # we tried, really, we tried + journal = Journal.objects.using(using).create(tag=tag, template=template, + message=unicode_truncate(message, 128)) + for name, value in kwargs.iteritems(): + if value is None: + continue + tag = Tag.objects.using(using).get_cached(name=name) + if isinstance(value, django.db.models.Model): + journal.objectdata_set.create(tag=tag, content_object=value) + else: + journal.stringdata_set.create(tag=tag, content=unicode(value)) + return journal + +def error_record(tag, tpl, **kwargs): + '''Records error events. + + You must use this function when logging error events. It uses another + database alias than the default one to be immune to transaction rollback + when logging in the middle of a transaction which is going to + rollback. + ''' + return record(tag, tpl, + using=getattr(settings, 'JOURNAL_DB_FOR_ERROR_ALIAS', 'default'), + **kwargs) diff --git a/django_journal/models.py b/django_journal/models.py index c711722..a39a25b 100644 --- a/django_journal/models.py +++ b/django_journal/models.py @@ -1,7 +1,7 @@ import string from django.db import models -from django.contrib.contenttypes import generic +from django.contrib.contenttypes.fields import GenericForeignKey from django.utils.translation import ugettext_lazy as _ import managers @@ -143,7 +143,7 @@ class ObjectData(models.Model): verbose_name=_('content type')) object_id = models.PositiveIntegerField(db_index=True, verbose_name=_('object id')) - content_object = generic.GenericForeignKey('content_type', + content_object = GenericForeignKey('content_type', 'object_id') class Meta: diff --git a/django_journal/tests.py b/django_journal/tests.py index 4fa4941..98b5de1 100644 --- a/django_journal/tests.py +++ b/django_journal/tests.py @@ -1,19 +1,17 @@ from django.test import TestCase from django.contrib.auth.models import User, Group -from django.db import transaction +from django.db.transaction import atomic - -from . import record -from . import actions -from . import models +from .main import record +from .actions import export_as_csv_generator +from .models import Journal class JournalTestCase(TestCase): def setUp(self): - models.JOURNAL_METADATA_CACHE_TIMEOUT = 0 self.users = [] self.groups = [] - with transaction.commit_on_success(): + with atomic(): for i in range(20): self.users.append( User.objects.create(username='user%s' % i)) @@ -30,27 +28,27 @@ class JournalTestCase(TestCase): record('logout', '{user} logged out', user=self.users[i]) def test_login(self): - for i, event in zip(range(20), models.Journal.objects.for_tag('login').order_by('id')): + for i, event in zip(range(20), Journal.objects.for_tag('login').order_by('id')): self.assertEqual(unicode(event), 'user{0} logged in'.format(i)) def test_groups(self): - for i, event in zip(range(40), models.Journal.objects.for_tag('group-changed').order_by('id')): + for i, event in zip(range(40), Journal.objects.for_tag('group-changed').order_by('id')): self.assertEqual(unicode(event), 'user{0} gave group group{0} to user{1}'.format(i, (i+1)%20)) def test_logout(self): - for i, event in zip(range(20), models.Journal.objects.for_tag('logout').order_by('id')): + for i, event in zip(range(20), Journal.objects.for_tag('logout').order_by('id')): self.assertEqual(unicode(event), 'user{0} logged out'.format(i)) def test_export_as_csv(self): - qs = models.Journal.objects.all() - l = list(actions.export_as_csv_generator(qs)) + qs = Journal.objects.all() + l = list(export_as_csv_generator(qs)) self.assertEquals(set(l[0]), set(['time', 'tag', 'message', 'group', 'group__id', 'user', 'user__id', 'user1', 'user1__id', 'user2', 'user2__id'])) - l = list(actions.export_as_csv_generator(qs[:5])) + l = list(export_as_csv_generator(qs[:5])) self.assertEquals(set(l[0]), set(['time', 'tag', 'message', 'user', 'user__id'])) for user in self.users: user.delete() - qs = models.Journal.objects.all() - l = list(actions.export_as_csv_generator(qs)) + qs = Journal.objects.all() + l = list(export_as_csv_generator(qs)) self.assertEquals(l[1]['user'], '') diff --git a/setup.py b/setup.py index 4e2ade6..c443f29 100755 --- a/setup.py +++ b/setup.py @@ -120,9 +120,9 @@ setup(name='django-journal', 'test': test }, install_requires=[ - 'django >= 1.7', + 'django >= 1.11,<2.0', 'django-model-utils', ], setup_requires=[ - 'django >= 1.4.2', + 'django >= 1.11,<2.0', ]) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..33239d7 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[testenv] +usedevelop = True +deps = + django<2.0 + pytest + pytest-django +setenv = + DJANGO_SETTINGS_MODULE=test_settings +commands = + py.test {posargs:django_journal/tests.py} -- 2.15.1