Added logging for all basic operations

This commit is contained in:
Raphael Michel
2015-12-12 22:35:25 +01:00
parent 83b5fa2fa6
commit 58b85819bc
18 changed files with 316 additions and 61 deletions

View File

@@ -50,7 +50,7 @@ class SettingsForm(forms.Form):
def __init__(self, *args, **kwargs):
self.obj = kwargs.pop('obj')
kwargs['initial'] = self.obj.settings
kwargs['initial'] = self.obj.settings.freeze()
super().__init__(*args, **kwargs)
def save(self):

View File

@@ -3,7 +3,8 @@ import json
from django import forms
from django.conf import settings
from django.db.models import SubfieldBase, TextField
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Model, QuerySet, SubfieldBase, TextField
from django.utils import translation
from django.utils.safestring import mark_safe
from typing import Dict, List
@@ -233,3 +234,15 @@ class I18nTextField(I18nFieldMixin, TextField, metaclass=SubfieldBase):
Like I18nCharField, but for TextFields.
"""
widget = I18nTextarea
class I18nJSONEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, LazyI18nString):
return obj.data
elif isinstance(obj, QuerySet):
return list(obj)
elif isinstance(obj, Model):
return {'type': obj.__class__.__name__, 'id': obj.id}
else:
return super().default(obj)

View File

@@ -5,6 +5,8 @@ from django.contrib.auth.models import (
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .base import LoggingMixin
class UserManager(BaseUserManager):
"""
@@ -30,7 +32,7 @@ class UserManager(BaseUserManager):
return user
class User(AbstractBaseUser, PermissionsMixin):
class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
"""
This is the user model used by pretix for authentication.

View File

@@ -1,3 +1,4 @@
import json
import uuid
from django.contrib.contenttypes.fields import GenericRelation
@@ -5,6 +6,8 @@ from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
from pretix.base.i18n import I18nJSONEncoder
def cachedfile_name(instance, filename: str) -> str:
return 'cachedfiles/%012d.%s' % (instance.id, filename.split('.')[-1])
@@ -29,10 +32,18 @@ def cached_file_delete(sender, instance, **kwargs):
instance.file.delete(False)
class LoggedModel(models.Model):
class LoggingMixin:
logentries = GenericRelation('LogEntry')
def log_action(self, user, action, data):
def log_action(self, action, data=None, user=None):
"""
Create a LogEntry object that is related to this object.
See the LogEntry documentation for details.
:param action: The namespaced action code
:param data: Any JSON-serializable object
:param user: The user performing the action (optional)
"""
from .log import LogEntry
from .event import Event
@@ -41,7 +52,13 @@ class LoggedModel(models.Model):
event = self
elif hasattr(self, 'event'):
event = self.event
LogEntry.objects.create(content_object=self, user=user, action=action, data=data, event=event)
l = LogEntry(content_object=self, user=user, action_type=action, event=event)
if data:
l.data = json.dumps(data, cls=I18nJSONEncoder)
l.save()
class LoggedModel(models.Model, LoggingMixin):
class Meta:
abstract = True

View File

@@ -2,7 +2,7 @@ import random
import string
from datetime import datetime
from django.db import models
from django.db import models, transaction
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from typing import List, Union
@@ -177,14 +177,6 @@ class Order(LoggedModel):
return True
return False # nothing there to modify
def mark_refunded(self):
"""
Mark this order as refunded. This sets the payment status and returns the order object.
"""
self.status = Order.STATUS_REFUNDED
self.save()
return self
def _can_be_paid(self) -> Union[bool, str]:
error_messages = {
'late': _("The payment is too late to be accepted."),

View File

@@ -359,6 +359,7 @@ class BasePaymentProvider:
:param order: The order object
"""
self.order.log_action('pretix.base.order.refunded')
return '<div class="alert alert-warning">%s</div>' % _('The money can not be automatically refunded, '
'please transfer the money back manually.')
@@ -379,7 +380,9 @@ class BasePaymentProvider:
:param request: The HTTP request
:param order: The order object
"""
order.mark_refunded()
from pretix.base.services.orders import mark_order_refunded
mark_order_refunded(order, user=request.user)
messages.success(request, _('The order has been marked as refunded. Please transfer the money '
'back to the buyer manually.'))
@@ -440,7 +443,9 @@ class FreeOrderProvider(BasePaymentProvider):
:param request: The HTTP request
:param order: The order object
"""
order.mark_refunded()
from pretix.base.services.orders import mark_order_refunded
mark_order_refunded(order, user=request.user)
messages.success(request, _('The order has been marked as refunded.'))
def is_allowed(self, request: HttpRequest) -> bool:

View File

@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from typing import List
from pretix.base.models import (
CartPosition, Event, EventLock, Order, OrderPosition, Quota,
CartPosition, Event, EventLock, Order, OrderPosition, Quota, User,
)
from pretix.base.payment import BasePaymentProvider
from pretix.base.services.mail import mail
@@ -31,7 +31,7 @@ error_messages = {
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
force: bool=False) -> Order:
force: bool=False, user: User=None) -> Order:
"""
Marks an order as paid. This sets the payment provider, info and date and returns
the order object.
@@ -46,6 +46,7 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
:param force: Whether this payment should be marked as paid even if no remaining
quota is available (default: ``False``).
:type force: boolean
:param user: The user that performed the change
:raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
"""
with order.event.lock():
@@ -59,6 +60,13 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
order.payment_manual = manual
order.status = Order.STATUS_PAID
order.save()
order.log_action('pretix.event.order.paid', {
'provider': provider,
'info': info,
'date': date,
'manual': manual,
'force': force
}, user=user)
order_paid.send(order.event, order=order)
mail(
@@ -78,6 +86,32 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
return order
@transaction.atomic
def mark_order_refunded(order: Order, user: User=None):
"""
Mark this order as refunded. This sets the payment status and returns the order object.
:param order: The order to change
:param user: The user that performed the change
"""
order.status = Order.STATUS_REFUNDED
order.save()
order.log_action('pretix.event.order.refunded', user=user)
return order
@transaction.atomic
def cancel_order(order: Order, user: User=None):
"""
Mark this order as canceled
:param order: The order to change
:param user: The user that performed the change
"""
order.status = Order.STATUS_CANCELLED
order.save()
order.log_action('pretix.event.order.cancelled', user=user)
return order
class OrderError(Exception):
pass
@@ -154,6 +188,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], dt: d
payment_provider=payment_provider.identifier
)
OrderPosition.transform_cart_positions(positions, order)
order.log_action('pretix.event.order.placed')
order_placed.send(event, order=order)
return order

View File

@@ -121,7 +121,20 @@ class SettingsProxy:
def _flush(self) -> None:
self._cached_obj = None
def freeze(self):
settings = {}
for key, v in DEFAULTS.items():
settings[key] = self._unserialize(v['default'], v['type'])
if self._parent:
settings.update(self._parent.settings.freeze())
for key, value in self._cache().items():
settings[key] = self.get(key)
return settings
def _unserialize(self, value: str, as_type: type) -> Any:
if as_type is None and value is not None and value.startswith('file://'):
as_type = File
if as_type is not None and isinstance(value, as_type):
return value
elif value is None:
@@ -186,15 +199,14 @@ class SettingsProxy:
if value is None and default is not None:
value = default
if as_type is None and value is not None and value.startswith('file://'):
as_type = File
return self._unserialize(value, as_type)
def __getitem__(self, key: str) -> Any:
return self.get(key)
def __getattr__(self, key: str) -> Any:
if key.startswith('_'):
return super().__getattr__(key)
return self.get(key)
def __setattr__(self, key: str, value: Any) -> None: