From 72332027586fb99a6dfbc0130cd34987096bd586 Mon Sep 17 00:00:00 2001 From: Paul Marillonnet Date: Thu, 15 Jul 2021 12:11:27 +0200 Subject: [PATCH] misc: apply black/isort/pyupgrade (#54260) --- django_journal/actions.py | 20 ++-- django_journal/admin.py | 97 ++++++++++-------- django_journal/decorator.py | 10 +- django_journal/journal.py | 51 +++++----- django_journal/managers.py | 56 +++++------ django_journal/middleware.py | 14 ++- django_journal/migrations/0001_initial.py | 65 +++++++++--- django_journal/models.py | 117 +++++++++++----------- setup.py | 69 ++++++------- test_settings.py | 16 ++- tests/test_main.py | 55 ++++++---- 11 files changed, 316 insertions(+), 254 deletions(-) diff --git a/django_journal/actions.py b/django_journal/actions.py index e5bdbf5..35a88ed 100644 --- a/django_journal/actions.py +++ b/django_journal/actions.py @@ -1,13 +1,12 @@ import csv - -from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponse from django.utils.encoding import force_text - +from django.utils.translation import ugettext_lazy as _ from . import models + 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)) @@ -15,13 +14,13 @@ def export_as_csv_generator(queryset): tags.add('%s__id' % tag) tags |= set(models.Tag.objects.filter(stringdata__journal__in=queryset).values_list('name', flat=True)) extra_headers = list(sorted(tags)) - yield header+extra_headers + yield header + extra_headers for journal in queryset: row = { - 'time': journal.time.isoformat(' '), - 'tag': force_text(journal.tag.name), - 'message': force_text(journal), - } + 'time': journal.time.isoformat(' '), + 'tag': force_text(journal.tag.name), + 'message': force_text(journal), + } for stringdata in journal.stringdata_set.all(): row_name = stringdata.tag.name.encode('utf-8') row[force_text(row_name)] = force_text(stringdata.content) @@ -34,6 +33,7 @@ def export_as_csv_generator(queryset): row[row_name] = force_text(objectdata.content_object) yield row + def export_as_csv(modeladmin, request, queryset): """ CSV export for journal @@ -47,4 +47,6 @@ def export_as_csv(modeladmin, request, queryset): for row in l: writer.writerow(row) return response -export_as_csv.short_description = _(u"Export CSV file") + + +export_as_csv.short_description = _("Export CSV file") diff --git a/django_journal/admin.py b/django_journal/admin.py index 638faec..2c83ed7 100644 --- a/django_journal/admin.py +++ b/django_journal/admin.py @@ -2,47 +2,48 @@ from string import Formatter import django.contrib.admin as admin from django.contrib.contenttypes.models import ContentType -from django.utils.html import format_html, escape, mark_safe from django.db import models +from django.urls import NoReverseMatch, reverse from django.utils.encoding import force_text +from django.utils.html import escape, format_html, mark_safe from django.utils.translation import ugettext_lazy as _ -from django.urls import reverse, NoReverseMatch -from .models import Journal, Tag, ObjectData, StringData from .actions import export_as_csv +from .models import Journal, ObjectData, StringData, Tag class ModelAdminFormatter(Formatter): - def __init__(self, model_admin=None, filter_link=True, - object_link=True): + def __init__(self, model_admin=None, filter_link=True, object_link=True): self.filter_link = filter_link self.object_link = object_link self.model_admin = model_admin - super(ModelAdminFormatter, self).__init__() + super().__init__() def build_object_link(self, value): content_type = ContentType.objects.get_for_model(value.__class__) - url = u'{0}:{1}_{2}_change'.format(self.model_admin.admin_site.name, - content_type.app_label, content_type.model) + url = '{}:{}_{}_change'.format( + self.model_admin.admin_site.name, content_type.app_label, content_type.model + ) try: url = reverse(url, args=(value.pk,)) except NoReverseMatch: - return u'' - return u''.format(escape(url)) + return '' + return f'' def format_field(self, value, format_spec): if isinstance(value, models.Model): res = '' if self.filter_link: content_type = ContentType.objects.get_for_model(value.__class__) - res = u'{2}'.format( - content_type.id, value.pk, escape(force_text(value))) + res = '{}'.format( + content_type.id, value.pk, escape(force_text(value)) + ) else: res = escape(force_text(value)) if self.object_link: res += self.build_object_link(value) return res - return escape(super(ModelAdminFormatter, self).format_field(value, format_spec)) + return escape(super().format_field(value, format_spec)) class ObjectDataInlineAdmin(admin.TabularInline): @@ -52,6 +53,7 @@ class ObjectDataInlineAdmin(admin.TabularInline): extra = 0 max_num = 0 + class StringDataInlineAdmin(admin.TabularInline): model = StringData fields = ('tag', 'content') @@ -59,91 +61,98 @@ class StringDataInlineAdmin(admin.TabularInline): extra = 0 max_num = 0 + class JournalAdmin(admin.ModelAdmin): list_display = ('time', '_tag', 'user', 'ip', 'message_for_list') list_filter = ('tag',) fields = ('time', 'tag', 'user', 'ip', 'message_for_change') readonly_fields = fields inlines = ( - ObjectDataInlineAdmin, - StringDataInlineAdmin, + ObjectDataInlineAdmin, + StringDataInlineAdmin, ) date_hierarchy = 'time' - search_fields = ('message','tag__name','time') - actions = [ export_as_csv ] + search_fields = ('message', 'tag__name', 'time') + actions = [export_as_csv] class Media: css = { - 'all': ('journal/css/journal.css',), + 'all': ('journal/css/journal.css',), } def queryset(self, request): '''Get as much data as possible using the fewest requests possible.''' - qs = super(JournalAdmin, self).queryset(request) - qs = qs.select_related('tag', 'template') \ - .prefetch_related('objectdata_set__content_type', - 'stringdata_set', 'objectdata_set__tag', - 'stringdata_set__tag', 'objectdata_set__content_object') + qs = super().queryset(request) + qs = qs.select_related('tag', 'template').prefetch_related( + 'objectdata_set__content_type', + 'stringdata_set', + 'objectdata_set__tag', + 'stringdata_set__tag', + 'objectdata_set__content_object', + ) return qs def lookup_allowed(self, key, *args, **kwargs): return True def _tag(self, entry): - name = entry.tag.name.replace(u'-', u'\u2011') - res = format_html('{1}', - escape(entry.tag.id), escape(name)) + name = entry.tag.name.replace('-', '\u2011') + res = format_html('{1}', escape(entry.tag.id), escape(name)) return res + _tag.short_description = _('tag') def ip(self, entry): '''Search and return any associated stringdata whose tag is "ip"''' for stringdata in entry.stringdata_set.all(): if stringdata.tag.name == 'ip': - return format_html('{ip}', - tag_id=stringdata.tag.id, ip=stringdata.content) + return format_html( + '{ip}', + tag_id=stringdata.tag.id, + ip=stringdata.content, + ) return _('None') + ip.short_description = _('IP') def user(self, entry): '''Search and return any associated objectdata whose tag is "user"''' for objectdata in entry.objectdata_set.all(): if objectdata.tag.name == 'user': - return format_html(self.object_filter_link(objectdata) + \ - self.object_link(objectdata)) + return format_html(self.object_filter_link(objectdata) + self.object_link(objectdata)) return _('None') + user.short_description = _('User') def object_filter_link(self, objectdata): if objectdata.content_object is not None: caption = force_text(objectdata.content_object) else: - caption = _(u'').format( - content_type=objectdata.content_type, - object_id=objectdata.object_id) - return u'{2}'.format( - objectdata.content_type_id, - objectdata.object_id, - escape(caption)) + caption = _('').format( + content_type=objectdata.content_type, object_id=objectdata.object_id + ) + return '{}'.format( + objectdata.content_type_id, objectdata.object_id, escape(caption) + ) def object_link(self, obj_data): if obj_data.content_object is None: - return u'' - url = u'{0}:{1}_{2}_change'.format(self.admin_site.name, - obj_data.content_type.app_label, - obj_data.content_type.model) + return '' + url = '{}:{}_{}_change'.format( + self.admin_site.name, obj_data.content_type.app_label, obj_data.content_type.model + ) try: url = reverse(url, args=(obj_data.object_id,)) except NoReverseMatch: return '' - return u''.format(url) + return f'' def message_for_change(self, entry): ctx = entry.message_context() formatter = ModelAdminFormatter(model_admin=self, filter_link=False) message = formatter.format(escape(entry.template.content), **ctx) return format_html('{}', mark_safe(message)) + message_for_change.short_description = _('Message') def message_for_list(self, entry): @@ -151,8 +160,10 @@ class JournalAdmin(admin.ModelAdmin): formatter = ModelAdminFormatter(model_admin=self) message = formatter.format(entry.template.content, **ctx) return format_html('{}', mark_safe(message)) + message_for_list.short_description = _('Message') message_for_list.admin_order_field = 'message' + admin.site.register(Journal, JournalAdmin) admin.site.register(Tag) diff --git a/django_journal/decorator.py b/django_journal/decorator.py index 824227a..cd9237a 100644 --- a/django_journal/decorator.py +++ b/django_journal/decorator.py @@ -1,10 +1,12 @@ from functools import wraps -from django.db import transaction, DEFAULT_DB_ALIAS + +from django.db import DEFAULT_DB_ALIAS, transaction if hasattr(transaction, 'atomic'): atomic = transaction.atomic else: - class Transaction(object): + + class Transaction: sid = None def __init__(self, using=None): @@ -41,8 +43,8 @@ else: def wrapper(*args, **kwargs): with self.__class__(using=self.using): return func(*args, **kwargs) - return wrapper + return wrapper def atomic(using=None): """ @@ -56,5 +58,3 @@ else: if callable(using): return Transaction(DEFAULT_DB_ALIAS)(using) return Transaction(using) - - diff --git a/django_journal/journal.py b/django_journal/journal.py index 69892a8..a05c127 100644 --- a/django_journal/journal.py +++ b/django_journal/journal.py @@ -1,8 +1,8 @@ import logging +import django.db.models from django.conf import settings from django.contrib.contenttypes.models import ContentType -import django.db.models from django.utils.encoding import force_text from .decorator import atomic @@ -11,32 +11,31 @@ 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.''' + """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. + """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 - ''' + 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 = force_text(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) as e: - raise JournalException( - 'Missing variable for the template message', template, 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-'): @@ -49,17 +48,19 @@ def record(tag, template, using=None, **kwargs): 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)) + 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.items(): 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_type=ContentType.objects.db_manager(using).get_for_model(value), - object_id=value.pk + tag=tag, + content_type=ContentType.objects.db_manager(using).get_for_model(value), + object_id=value.pk, ) else: journal.stringdata_set.create(tag=tag, content=force_text(value)) @@ -67,13 +68,13 @@ def record(tag, template, using=None, **kwargs): def error_record(tag, tpl, **kwargs): - '''Records error events. + """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. - ''' + 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. + """ if kwargs.get('using') is None: kwargs['using'] = getattr(settings, 'JOURNAL_DB_FOR_ERROR_ALIAS', 'default') diff --git a/django_journal/managers.py b/django_journal/managers.py index 10aea73..86f2052 100644 --- a/django_journal/managers.py +++ b/django_journal/managers.py @@ -1,6 +1,6 @@ from django.contrib.contenttypes.models import ContentType +from django.db.models import Manager, Q from django.db.models.query import QuerySet -from django.db.models import Q, Manager class CachedQuerySet(QuerySet): @@ -27,34 +27,29 @@ class JournalQuerySet(QuerySet): '''Return Journal records linked to this object.''' content_type = ContentType.objects.get_for_model(obj) if tag is None: - return self.filter(objectdata__content_type=content_type, - objectdata__object_id=obj.pk) + return self.filter(objectdata__content_type=content_type, objectdata__object_id=obj.pk) else: return self.filter( - objectdata__tag__name=tag, - objectdata__content_type=content_type, - objectdata__object_id=obj.pk) + objectdata__tag__name=tag, objectdata__content_type=content_type, objectdata__object_id=obj.pk + ) def for_objects(self, objects): - '''Return journal records linked to any of this objects. + """Return journal records linked to any of this objects. - All objects must have the same model. - ''' + All objects must have the same model. + """ if not objects: return self.none() - content_types = [ ContentType.objects.get_for_model(obj) - for obj in objects ] + content_types = [ContentType.objects.get_for_model(obj) for obj in objects] if len(set(content_types)) != 1: raise ValueError('objects must have of the same content type') - pks = [ obj.pk for obj in objects ] - return self.filter( - objectdata__content_type=content_types[0], - objectdata__object_id__in=pks) + pks = [obj.pk for obj in objects] + return self.filter(objectdata__content_type=content_types[0], objectdata__object_id__in=pks) def for_tag(self, tag): - '''Returns Journal records linked to this tag by their own tag or - the tag on their data records. - ''' + """Returns Journal records linked to this tag by their own tag or + the tag on their data records. + """ from . import models if not isinstance(tag, models.Tag): @@ -64,17 +59,22 @@ class JournalQuerySet(QuerySet): return self.none() # always remember: multiple join (OR in WHERE) produces duplicate # lines ! Use .distinct() for safety. - return self.filter(Q(tag=tag)| - Q(objectdata__tag=tag)| - Q(stringdata__tag=tag)) \ - .distinct() + return self.filter(Q(tag=tag) | Q(objectdata__tag=tag) | Q(stringdata__tag=tag)).distinct() class JournalManager(Manager.from_queryset(JournalQuerySet)): def get_query_set(self): - return super(JournalManager, self).get_query_set() \ - .prefetch_related('objectdata_set__content_type', - 'stringdata_set', 'objectdata_set__tag', - 'stringdata_set__tag', 'objectdata_set__content_object', - 'tag', 'template') \ - .select_related('tag', 'template') + return ( + super() + .get_query_set() + .prefetch_related( + 'objectdata_set__content_type', + 'stringdata_set', + 'objectdata_set__tag', + 'stringdata_set__tag', + 'objectdata_set__content_object', + 'tag', + 'template', + ) + .select_related('tag', 'template') + ) diff --git a/django_journal/middleware.py b/django_journal/middleware.py index c3b8be0..096b1f1 100644 --- a/django_journal/middleware.py +++ b/django_journal/middleware.py @@ -1,29 +1,33 @@ from django.utils.deprecation import MiddlewareMixin + from django_journal import journal class JournalMiddleware(MiddlewareMixin): - '''Add record and error_record methods to the request object to log - current user and current REMOTE_ADRESS. + """Add record and error_record methods to the request object to log + current user and current REMOTE_ADRESS. - It must be setup after the auth middleware. - ''' + It must be setup after the auth middleware. + """ def process_request(self, request): user = getattr(request, 'user', None) ip = request.META.get('REMOTE_ADDR', None) + def record(tag, template, using=None, **kwargs): if 'user' not in kwargs: kwargs['user'] = user if 'ip' not in kwargs: kwargs['ip'] = ip - journal.record(tag, template, using=using,**kwargs) + journal.record(tag, template, using=using, **kwargs) + def error_record(tag, template, using=None, **kwargs): if 'user' not in kwargs: kwargs['user'] = user if 'ip' not in kwargs: kwargs['ip'] = ip journal.error_record(tag, template, using=using, **kwargs) + request.record = record request.error_record = error_record return None diff --git a/django_journal/migrations/0001_initial.py b/django_journal/migrations/0001_initial.py index 8a5def2..9a2f303 100644 --- a/django_journal/migrations/0001_initial.py +++ b/django_journal/migrations/0001_initial.py @@ -1,8 +1,5 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): @@ -15,7 +12,10 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Journal', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', + models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True), + ), ('time', models.DateTimeField(auto_now_add=True, verbose_name='time', db_index=True)), ('message', models.CharField(max_length=128, verbose_name='message', db_index=True)), ], @@ -28,10 +28,23 @@ class Migration(migrations.Migration): migrations.CreateModel( name='ObjectData', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', + models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True), + ), ('object_id', models.PositiveIntegerField(verbose_name='object id', db_index=True)), - ('content_type', models.ForeignKey(verbose_name='content type', to='contenttypes.ContentType', on_delete=models.CASCADE)), - ('journal', models.ForeignKey(verbose_name='journal entry', to='django_journal.Journal', on_delete=models.CASCADE)), + ( + 'content_type', + models.ForeignKey( + verbose_name='content type', to='contenttypes.ContentType', on_delete=models.CASCADE + ), + ), + ( + 'journal', + models.ForeignKey( + verbose_name='journal entry', to='django_journal.Journal', on_delete=models.CASCADE + ), + ), ], options={ 'verbose_name': 'linked object', @@ -40,9 +53,17 @@ class Migration(migrations.Migration): migrations.CreateModel( name='StringData', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', + models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True), + ), ('content', models.TextField(verbose_name='content')), - ('journal', models.ForeignKey(verbose_name='journal entry', to='django_journal.Journal', on_delete=models.CASCADE)), + ( + 'journal', + models.ForeignKey( + verbose_name='journal entry', to='django_journal.Journal', on_delete=models.CASCADE + ), + ), ], options={ 'verbose_name': 'linked text string', @@ -51,7 +72,10 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Tag', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', + models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True), + ), ('name', models.CharField(unique=True, max_length=32, verbose_name='name', db_index=True)), ], options={ @@ -62,7 +86,10 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Template', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ( + 'id', + models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True), + ), ('content', models.TextField(unique=True, verbose_name='content', db_index=True)), ], options={ @@ -82,19 +109,25 @@ class Migration(migrations.Migration): migrations.AddField( model_name='journal', name='tag', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, verbose_name='tag', to='django_journal.Tag'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, verbose_name='tag', to='django_journal.Tag' + ), ), migrations.AddField( model_name='journal', name='template', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, verbose_name='template', to='django_journal.Template'), + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + verbose_name='template', + to='django_journal.Template', + ), ), migrations.AlterUniqueTogether( name='stringdata', - unique_together=set([('journal', 'tag')]), + unique_together={('journal', 'tag')}, ), migrations.AlterUniqueTogether( name='objectdata', - unique_together=set([('journal', 'tag')]), + unique_together={('journal', 'tag')}, ), ] diff --git a/django_journal/models.py b/django_journal/models.py index 10840bf..6bbe532 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.fields import GenericForeignKey +from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ @@ -10,14 +10,14 @@ from . import managers @python_2_unicode_compatible class Tag(models.Model): - '''Tag allows typing event and data linked to events. + """Tag allows typing event and data linked to events. + + name: + the string identifier of the tag + """ - name: - the string identifier of the tag - ''' objects = managers.TagManager() - name = models.CharField(verbose_name=_('name'), max_length=32, unique=True, - db_index=True) + name = models.CharField(verbose_name=_('name'), max_length=32, unique=True, db_index=True) def __str__(self): return self.name @@ -32,14 +32,14 @@ class Tag(models.Model): @python_2_unicode_compatible class Template(models.Model): - '''Template for formatting an event. + """Template for formatting an event. + + ex.: Template( + content='{user1} gave group {group} to {user2}') + """ - ex.: Template( - content='{user1} gave group {group} to {user2}') - ''' objects = managers.TemplateManager() - content = models.TextField(verbose_name=_('content'), unique=True, - db_index=True) + content = models.TextField(verbose_name=_('content'), unique=True, db_index=True) def __str__(self): return self.content @@ -53,26 +53,23 @@ class Template(models.Model): @python_2_unicode_compatible class Journal(models.Model): - '''One line of the journal. + """One line of the journal. - Each recorded event in the journal is a Journal instance. + Each recorded event in the journal is a Journal instance. + + time - the time at which the event was recorded + tag - the tag giving the type of event + template - a format string to present the event + message - a simple string representation of the event, computed using + the template and associated datas. + """ - time - the time at which the event was recorded - tag - the tag giving the type of event - template - a format string to present the event - message - a simple string representation of the event, computed using - the template and associated datas. - ''' objects = managers.JournalManager() - time = models.DateTimeField(verbose_name=_('time'), auto_now_add=True, - db_index=True) - tag = models.ForeignKey(Tag, verbose_name=_('tag'), - on_delete=models.PROTECT) - template = models.ForeignKey(Template, verbose_name=_('template'), - on_delete=models.PROTECT) - message = models.CharField(verbose_name=_('message'), max_length=128, - db_index=True) + time = models.DateTimeField(verbose_name=_('time'), auto_now_add=True, db_index=True) + tag = models.ForeignKey(Tag, verbose_name=_('tag'), on_delete=models.PROTECT) + template = models.ForeignKey(Template, verbose_name=_('template'), on_delete=models.PROTECT) + message = models.CharField(verbose_name=_('message'), max_length=128, db_index=True) class Meta: ordering = ('-id',) @@ -85,8 +82,9 @@ class Journal(models.Model): if data.content_object is not None: ctx[data.tag.name] = data.content_object else: - ctx[data.tag.name] = u''.format( - content_type=data.content_type, object_id=data.object_id) + ctx[data.tag.name] = ''.format( + content_type=data.content_type, object_id=data.object_id + ) for data in self.stringdata_set.all(): ctx[data.tag.name] = data.content for text, field, format_spec, conversion in string.Formatter().parse(self.template.content): @@ -98,30 +96,29 @@ class Journal(models.Model): return ctx def add_object_tag(self, tag_name, obj): - ObjectData(journal=self, - tag=Tag.objects.get_cached(name=tag_name), - content_object=obj).save() + ObjectData(journal=self, tag=Tag.objects.get_cached(name=tag_name), content_object=obj).save() def __str__(self): ctx = self.message_context() return self.template.content.format(**ctx) def __repr__(self): - return ''.format( - self.pk, unicode(self.tag).encode('utf-8'), - unicode(self.message).encode('utf-8')) + return ''.format( + self.pk, unicode(self.tag).encode('utf-8'), unicode(self.message).encode('utf-8') + ) class StringData(models.Model): - '''String data associated to a recorded event. + """String data associated to a recorded event. + + journal: + the recorded event + tag: + the identifier for this data + content: + the string value of the data + """ - journal: - the recorded event - tag: - the identifier for this data - content: - the string value of the data - ''' journal = models.ForeignKey(Journal, verbose_name=_('journal entry'), on_delete=models.CASCADE) tag = models.ForeignKey(Tag, verbose_name=_('tag'), on_delete=models.CASCADE) content = models.TextField(verbose_name=_('content')) @@ -133,27 +130,27 @@ class StringData(models.Model): @python_2_unicode_compatible class ObjectData(models.Model): - '''Object data associated with a recorded event. + """Object data associated with a recorded event. + + journal: + the recorded event + tag: + the identifier for this data + content_object: + the object value of the data + """ - journal: - the recorded event - tag: - the identifier for this data - content_object: - the object value of the data - ''' journal = models.ForeignKey(Journal, verbose_name=_('journal entry'), on_delete=models.CASCADE) tag = models.ForeignKey(Tag, verbose_name=_('tag'), on_delete=models.CASCADE) - content_type = models.ForeignKey('contenttypes.ContentType', on_delete=models.CASCADE, - verbose_name=_('content type')) - object_id = models.PositiveIntegerField(db_index=True, - verbose_name=_('object id')) - content_object = GenericForeignKey('content_type', - 'object_id') + content_type = models.ForeignKey( + 'contenttypes.ContentType', on_delete=models.CASCADE, verbose_name=_('content type') + ) + object_id = models.PositiveIntegerField(db_index=True, verbose_name=_('object id')) + content_object = GenericForeignKey('content_type', 'object_id') class Meta: unique_together = (('journal', 'tag'),) verbose_name = _('linked object') def __str__(self): - return u'{0}:{1}:{2}'.format(self.journal.id, self.tag, self.content_object) + return f'{self.journal.id}:{self.tag}:{self.content_object}' diff --git a/setup.py b/setup.py index 6528369..f740cb2 100755 --- a/setup.py +++ b/setup.py @@ -2,12 +2,12 @@ import os import subprocess import sys - -from setuptools import setup, find_packages -from setuptools.command.install_lib import install_lib as _install_lib -from distutils.command.build import build as _build -from setuptools.command.sdist import sdist from distutils.cmd import Command +from distutils.command.build import build as _build + +from setuptools import find_packages, setup +from setuptools.command.install_lib import install_lib as _install_lib +from setuptools.command.sdist import sdist class test(Command): @@ -22,6 +22,7 @@ class test(Command): def run(self): import os + try: from django.core.management import call_command except ImportError: @@ -45,6 +46,7 @@ class compile_translations(Command): try: os.environ.pop('DJANGO_SETTINGS_MODULE', None) from django.core.management import call_command + os.chdir(os.path.realpath('django_journal')) call_command('compilemessages') except ImportError: @@ -80,16 +82,18 @@ class eo_sdist(sdist): def get_version(): - '''Use the VERSION, if absent generates a version with git describe, if not - tag exists, take 0.0- and add the length of the commit log. - ''' + """Use the VERSION, if absent generates a version with git describe, if not + tag exists, take 0.0- and add the length of the commit log. + """ if os.path.exists('VERSION'): - with open('VERSION', 'r') as v: + with open('VERSION') as v: return v.read() if os.path.exists('.git'): p = subprocess.Popen( ['git', 'describe', '--dirty=.dirty', '--match=v*'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) result = p.communicate()[0] if p.returncode == 0: result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v @@ -100,30 +104,27 @@ def get_version(): version = result return version else: - return '0.0.post%s' % len( - subprocess.check_output( - ['git', 'rev-list', 'HEAD']).splitlines()) + return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines()) return '0.0' -setup(name='django-journal', - version=get_version(), - license='AGPLv3', - description='Keep a structured -- i.e. not just log strings -- journal' - ' of events in your applications', - url='http://dev.entrouvert.org/projects/django-journal/', - download_url='http://repos.entrouvert.org/django-journal.git/', - author="Entr'ouvert", - author_email="info@entrouvert.com", - packages=find_packages(os.path.dirname(__file__) or '.'), - include_package_data=True, - cmdclass={ - 'build': build, - 'install_lib': install_lib, - 'compile_translations': compile_translations, - 'sdist': eo_sdist, - 'test': test - }, - install_requires=[ - 'django >= 1.11,<2.3' - ]) +setup( + name='django-journal', + version=get_version(), + license='AGPLv3', + description='Keep a structured -- i.e. not just log strings -- journal' ' of events in your applications', + url='http://dev.entrouvert.org/projects/django-journal/', + download_url='http://repos.entrouvert.org/django-journal.git/', + author="Entr'ouvert", + author_email="info@entrouvert.com", + packages=find_packages(os.path.dirname(__file__) or '.'), + include_package_data=True, + cmdclass={ + 'build': build, + 'install_lib': install_lib, + 'compile_translations': compile_translations, + 'sdist': eo_sdist, + 'test': test, + }, + install_requires=['django >= 1.11,<2.3'], +) diff --git a/test_settings.py b/test_settings.py index f0902ed..b1ca36e 100644 --- a/test_settings.py +++ b/test_settings.py @@ -1,16 +1,12 @@ INSTALLED_APPS = ( - 'django_journal', 'django.contrib.contenttypes', 'django.contrib.auth', - 'django.contrib.sessions' + 'django_journal', + 'django.contrib.contenttypes', + 'django.contrib.auth', + 'django.contrib.sessions', ) DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': '_test' - }, - 'error': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': '_test' - } + 'default': {'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': '_test'}, + 'error': {'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': '_test'}, } SECRET_KEY = "django_tests_secret_key" diff --git a/tests/test_main.py b/tests/test_main.py index 766b4cd..4491db9 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,11 +1,10 @@ -from django.test import TestCase -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import Group, User from django.db import transaction +from django.test import TestCase from django.utils.encoding import force_text - -from django_journal.journal import record, error_record from django_journal.actions import export_as_csv_generator +from django_journal.journal import error_record, record from django_journal.models import Journal, Tag @@ -15,39 +14,57 @@ class JournalTestCase(TestCase): self.groups = [] with transaction.atomic(): for i in range(20): - self.users.append( - User.objects.create(username='user%s' % i)) + self.users.append(User.objects.create(username='user%s' % i)) for i in range(20): - self.groups.append( - Group.objects.create(name='group%s' % i)) + self.groups.append(Group.objects.create(name='group%s' % i)) for i in range(20): record('login', '{user} logged in', user=self.users[i]) for i in range(20): - record('group-changed', '{user1} gave group {group} to {user2}', - user1=self.users[i], group=self.groups[i], - user2=self.users[(i+1) % 20]) + record( + 'group-changed', + '{user1} gave group {group} to {user2}', + user1=self.users[i], + group=self.groups[i], + user2=self.users[(i + 1) % 20], + ) for i in range(20): record('logout', '{user} logged out', user=self.users[i]) def test_login(self): for i, event in zip(range(20), Journal.objects.for_tag('login').order_by('id')): - self.assertEqual(force_text(event), 'user{0} logged in'.format(i)) + self.assertEqual(force_text(event), f'user{i} logged in') def test_groups(self): for i, event in zip(range(40), Journal.objects.for_tag('group-changed').order_by('id')): - self.assertEqual(force_text(event), - 'user{0} gave group group{0} to user{1}'.format(i, (i+1)%20)) + self.assertEqual( + force_text(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), Journal.objects.for_tag('logout').order_by('id')): - self.assertEqual(force_text(event), 'user{0} logged out'.format(i)) + self.assertEqual(force_text(event), f'user{i} logged out') def test_export_as_csv(self): 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'])) + self.assertEquals( + set(l[0]), + { + 'time', + 'tag', + 'message', + 'group', + 'group__id', + 'user', + 'user__id', + 'user1', + 'user1__id', + 'user2', + 'user2__id', + }, + ) l = list(export_as_csv_generator(qs[:5])) - self.assertEquals(set(l[0]), set(['time', 'tag', 'message', 'user', 'user__id'])) + self.assertEquals(set(l[0]), {'time', 'tag', 'message', 'user', 'user__id'}) for user in self.users: user.delete() qs = Journal.objects.all() @@ -58,8 +75,8 @@ class JournalTestCase(TestCase): def test_error_record(db): error_record('error', 'error message') journal = Journal.objects.first() - assert journal.tag.name == u'error' - assert journal.message == u'error message' + assert journal.tag.name == 'error' + assert journal.message == 'error message' # specifying None as database use the defaut one error_record('error', 'error message', using=None)