Add generic error log model.
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -2,7 +2,14 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import SharedUploadedFile, UserLocaleProfile
|
||||
from .models import ErrorLogEntry, SharedUploadedFile, UserLocaleProfile
|
||||
|
||||
|
||||
@admin.register(ErrorLogEntry)
|
||||
class ErrorLogEntryAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = 'datetime'
|
||||
list_display = ('namespace', 'content_object', 'datetime', 'result')
|
||||
readonly_fields = list_display
|
||||
|
||||
|
||||
@admin.register(SharedUploadedFile)
|
||||
|
||||
@@ -14,6 +14,7 @@ from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.celery import app
|
||||
from navigation import SourceColumn
|
||||
from navigation.classes import Separator, Text
|
||||
from rest_api.classes import APIEndPoint
|
||||
|
||||
@@ -85,8 +86,27 @@ class CommonApp(MayanAppConfig):
|
||||
def ready(self):
|
||||
super(CommonApp, self).ready()
|
||||
|
||||
ErrorLogEntry = self.get_model(model_name='ErrorLogEntry')
|
||||
|
||||
APIEndPoint(app=self, version_string='1')
|
||||
|
||||
SourceColumn(
|
||||
source=ErrorLogEntry, label=_('Namespace'),
|
||||
attribute='namespace'
|
||||
)
|
||||
SourceColumn(
|
||||
source=ErrorLogEntry, label=_('Object'),
|
||||
attribute='content_object'
|
||||
)
|
||||
SourceColumn(
|
||||
source=ErrorLogEntry, label=_('Date and time'),
|
||||
attribute='datetime'
|
||||
)
|
||||
SourceColumn(
|
||||
source=ErrorLogEntry, label=_('Result'),
|
||||
attribute='result'
|
||||
)
|
||||
|
||||
app.conf.CELERYBEAT_SCHEDULE.update(
|
||||
{
|
||||
'task_delete_stale_uploads': {
|
||||
|
||||
@@ -100,6 +100,82 @@ class DashboardWidget(object):
|
||||
self.__class__._registry.append(self)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ErrorLogNamespace(object):
|
||||
def __init__(self, name, label=None):
|
||||
self.name = name
|
||||
self.label = label or name
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.label)
|
||||
|
||||
def all(self):
|
||||
ErrorLogEntry = apps.get_model(
|
||||
app_label='common', model_name='ErrorLogEntry'
|
||||
)
|
||||
|
||||
return ErrorLogEntry.objects.filter(namespace=self.name)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Filter(object):
|
||||
_registry = {}
|
||||
|
||||
@classmethod
|
||||
def get(cls, slug):
|
||||
return cls._registry[slug]
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return cls._registry
|
||||
|
||||
def __init__(self, label, slug, filter_kwargs, model, object_permission=None, hide_links=False):
|
||||
self.label = label
|
||||
self.slug = slug
|
||||
self.filter_kwargs = filter_kwargs
|
||||
self.model = model
|
||||
self.object_permission = object_permission
|
||||
self.hide_links = hide_links
|
||||
|
||||
self.__class__._registry[self.slug] = self
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.label)
|
||||
|
||||
def get_queryset(self, user):
|
||||
AccessControlList = apps.get_model(
|
||||
app_label='acls', model_name='AccessControlList'
|
||||
)
|
||||
|
||||
queryset = self.model.objects.all()
|
||||
for kwargs in self.filter_kwargs:
|
||||
queryset = queryset.filter(**kwargs)
|
||||
|
||||
queryset = queryset.distinct()
|
||||
|
||||
if self.object_permission:
|
||||
return AccessControlList.objects.filter_by_access(
|
||||
self.object_permission, user, queryset=queryset
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
|
||||
|
||||
class MissingItem(object):
|
||||
_registry = []
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
return cls._registry
|
||||
|
||||
def __init__(self, label, condition, description, view):
|
||||
self.label = label
|
||||
self.condition = condition
|
||||
self.description = description
|
||||
self.view = view
|
||||
self.__class__._registry.append(self)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ModelAttribute(object):
|
||||
__registry = {}
|
||||
@@ -179,65 +255,6 @@ class ModelAttribute(object):
|
||||
self.__registry[model][type_name].append(self)
|
||||
|
||||
|
||||
class MissingItem(object):
|
||||
_registry = []
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
return cls._registry
|
||||
|
||||
def __init__(self, label, condition, description, view):
|
||||
self.label = label
|
||||
self.condition = condition
|
||||
self.description = description
|
||||
self.view = view
|
||||
self.__class__._registry.append(self)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Filter(object):
|
||||
_registry = {}
|
||||
|
||||
@classmethod
|
||||
def get(cls, slug):
|
||||
return cls._registry[slug]
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return cls._registry
|
||||
|
||||
def __init__(self, label, slug, filter_kwargs, model, object_permission=None, hide_links=False):
|
||||
self.label = label
|
||||
self.slug = slug
|
||||
self.filter_kwargs = filter_kwargs
|
||||
self.model = model
|
||||
self.object_permission = object_permission
|
||||
self.hide_links = hide_links
|
||||
|
||||
self.__class__._registry[self.slug] = self
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.label)
|
||||
|
||||
def get_queryset(self, user):
|
||||
AccessControlList = apps.get_model(
|
||||
app_label='acls', model_name='AccessControlList'
|
||||
)
|
||||
|
||||
queryset = self.model.objects.all()
|
||||
for kwargs in self.filter_kwargs:
|
||||
queryset = queryset.filter(**kwargs)
|
||||
|
||||
queryset = queryset.distinct()
|
||||
|
||||
if self.object_permission:
|
||||
return AccessControlList.objects.filter_by_access(
|
||||
self.object_permission, user, queryset=queryset
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
|
||||
|
||||
class Package(object):
|
||||
_registry = []
|
||||
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from navigation import Link
|
||||
|
||||
from .permissions_runtime import permission_error_log_view
|
||||
|
||||
|
||||
def get_kwargs_factory(variable_name):
|
||||
def get_kwargs(context):
|
||||
ContentType = apps.get_model(
|
||||
app_label='contenttypes', model_name='ContentType'
|
||||
)
|
||||
|
||||
content_type = ContentType.objects.get_for_model(
|
||||
context[variable_name]
|
||||
)
|
||||
return {
|
||||
'app_label': '"{}"'.format(content_type.app_label),
|
||||
'model': '"{}"'.format(content_type.model),
|
||||
'object_id': '{}.pk'.format(variable_name)
|
||||
}
|
||||
|
||||
return get_kwargs
|
||||
|
||||
|
||||
link_about = Link(
|
||||
icon='fa fa-info', text=_('About this'), view='common:about_view'
|
||||
@@ -35,6 +56,15 @@ link_documentation = Link(
|
||||
icon='fa fa-book', tags='new_window', text=_('Documentation'),
|
||||
url='https://mayan.readthedocs.io/en/stable/'
|
||||
)
|
||||
link_error_list = Link(
|
||||
permissions=(permission_error_log_view,), text=_('Errors'),
|
||||
view='common:error_list', kwargs=get_kwargs_factory('resolved_object')
|
||||
)
|
||||
link_error_list_with_icon = Link(
|
||||
icon='fa fa-lock', permissions=(permission_error_log_view,),
|
||||
text=_('Erros'), view='common:error_list',
|
||||
kwargs=get_kwargs_factory('resolved_object')
|
||||
)
|
||||
link_filters = Link(
|
||||
icon='fa fa-filter', text=_('Data filters'),
|
||||
view='common:filter_selection'
|
||||
|
||||
14
mayan/apps/common/managers.py
Normal file
14
mayan/apps/common/managers.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
|
||||
class ErrorLogEntryManager(models.Manager):
|
||||
def register(self, model):
|
||||
ErrorLogEntry = apps.get_model(
|
||||
app_label='common', model_name='ErrorLogEntry'
|
||||
)
|
||||
model.add_to_class('error_logs', GenericRelation(ErrorLogEntry))
|
||||
33
mayan/apps/common/migrations/0008_errorlogentry.py
Normal file
33
mayan/apps/common/migrations/0008_errorlogentry.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-08-25 06:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('common', '0007_auto_20170118_1758'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ErrorLogEntry',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('namespace', models.CharField(max_length=128, verbose_name='Namespace')),
|
||||
('object_id', models.PositiveIntegerField(blank=True, null=True)),
|
||||
('datetime', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Date time')),
|
||||
('result', models.TextField(blank=True, null=True, verbose_name='Result')),
|
||||
('content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='error_log_content_type', to='contenttypes.ContentType')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('datetime',),
|
||||
'verbose_name': 'Error log entry',
|
||||
'verbose_name_plural': 'Error log entries',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -5,10 +5,13 @@ import uuid
|
||||
from pytz import common_timezones
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
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 .managers import ErrorLogEntryManager
|
||||
from .runtime import shared_storage_backend
|
||||
|
||||
|
||||
@@ -16,6 +19,31 @@ def upload_to(instance, filename):
|
||||
return 'shared-file-{}'.format(uuid.uuid4().hex)
|
||||
|
||||
|
||||
class ErrorLogEntry(models.Model):
|
||||
namespace = models.CharField(
|
||||
max_length=128, verbose_name=_('Namespace')
|
||||
)
|
||||
content_type = models.ForeignKey(
|
||||
ContentType, blank=True, on_delete=models.CASCADE, null=True,
|
||||
related_name='error_log_content_type'
|
||||
)
|
||||
object_id = models.PositiveIntegerField(blank=True, null=True)
|
||||
content_object = GenericForeignKey(
|
||||
ct_field='content_type', fk_field='object_id',
|
||||
)
|
||||
datetime = models.DateTimeField(
|
||||
auto_now_add=True, db_index=True, verbose_name=_('Date time')
|
||||
)
|
||||
result = models.TextField(blank=True, null=True, verbose_name=_('Result'))
|
||||
|
||||
objects = ErrorLogEntryManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ('datetime',)
|
||||
verbose_name = _('Error log entry')
|
||||
verbose_name_plural = _('Error log entries')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class SharedUploadedFile(models.Model):
|
||||
file = models.FileField(
|
||||
@@ -60,9 +88,9 @@ class UserLocaleProfile(models.Model):
|
||||
choices=settings.LANGUAGES, max_length=8, verbose_name=_('Language')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.user)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('User locale profile')
|
||||
verbose_name_plural = _('User locale profiles')
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.user)
|
||||
|
||||
12
mayan/apps/common/permissions_runtime.py
Normal file
12
mayan/apps/common/permissions_runtime.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from permissions import PermissionNamespace
|
||||
|
||||
namespace = PermissionNamespace('common', _('Common'))
|
||||
|
||||
permission_error_log_view = namespace.add_permission(
|
||||
name='error_log_view', label=_('View errorr log')
|
||||
)
|
||||
|
||||
@@ -7,9 +7,9 @@ from .api_views import APIContentTypeList
|
||||
from .views import (
|
||||
AboutView, CheckVersionView, CurrentUserDetailsView, CurrentUserEditView,
|
||||
CurrentUserLocaleProfileDetailsView, CurrentUserLocaleProfileEditView,
|
||||
FaviconRedirectView, FilterResultListView, FilterSelectView, HomeView,
|
||||
LicenseView, PackagesLicensesView, SetupListView, ToolsListView,
|
||||
multi_object_action_view
|
||||
ErrorLogEntryListView, FaviconRedirectView, FilterResultListView,
|
||||
FilterSelectView, HomeView, LicenseView, PackagesLicensesView,
|
||||
SetupListView, ToolsListView, multi_object_action_view
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@@ -54,6 +54,10 @@ urlpatterns = [
|
||||
r'^filter/(?P<slug>[\w-]+)/results/$', FilterResultListView.as_view(),
|
||||
name='filter_results'
|
||||
),
|
||||
url(
|
||||
r'^(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/errors/$',
|
||||
ErrorLogEntryListView.as_view(), name='error_list'
|
||||
),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
|
||||
@@ -4,8 +4,9 @@ from json import dumps
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.shortcuts import resolve_url
|
||||
from django.shortcuts import get_object_or_404, resolve_url
|
||||
from django.template import RequestContext
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone, translation
|
||||
@@ -13,6 +14,8 @@ from django.utils.http import urlencode
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
from django.views.generic import RedirectView, TemplateView
|
||||
|
||||
from acls.models import AccessControlList
|
||||
|
||||
from .classes import Filter
|
||||
from .exceptions import NotLatestVersion
|
||||
from .forms import (
|
||||
@@ -28,6 +31,8 @@ from .generics import ( # NOQA
|
||||
SingleObjectEditView, SingleObjectListView, SimpleView
|
||||
)
|
||||
from .menus import menu_tools, menu_setup
|
||||
from .models import ErrorLogEntry
|
||||
from .permissions_runtime import permission_error_log_view
|
||||
from .utils import check_version
|
||||
|
||||
|
||||
@@ -130,6 +135,43 @@ class CurrentUserLocaleProfileEditView(SingleObjectEditView):
|
||||
return self.request.user.locale_profile
|
||||
|
||||
|
||||
class ErrorLogEntryListView(SingleObjectListView):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.object_content_type = get_object_or_404(
|
||||
ContentType, app_label=self.kwargs['app_label'],
|
||||
model=self.kwargs['model']
|
||||
)
|
||||
|
||||
try:
|
||||
self.content_object = self.object_content_type.get_object_for_this_type(
|
||||
pk=self.kwargs['object_id']
|
||||
)
|
||||
except self.object_content_type.model_class().DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
AccessControlList.objects.check_access(
|
||||
obj=self.content_object, permissions=permission_error_log_view,
|
||||
user=request.user
|
||||
)
|
||||
|
||||
return super(ErrorLogEntryListView, self).dispatch(
|
||||
request, *args, **kwargs
|
||||
)
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'hide_object': True,
|
||||
'object': self.content_object,
|
||||
'title': _('Error log entries for: %s' % self.content_object),
|
||||
}
|
||||
|
||||
def get_object_list(self):
|
||||
return ErrorLogEntry.objects.filter(
|
||||
content_type=self.object_content_type,
|
||||
object_id=self.content_object.pk
|
||||
)
|
||||
|
||||
|
||||
class FaviconRedirectView(RedirectView):
|
||||
permanent = True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user