Admin cosmetics
This commit is contained in:
parent
c731a73889
commit
ccbda512bf
|
@ -62,15 +62,15 @@ def wrap_admin_view(modeladmin, view):
|
||||||
def set_default_filter(queryarg, request, value):
|
def set_default_filter(queryarg, request, value):
|
||||||
""" set default filters for changelist_view """
|
""" set default filters for changelist_view """
|
||||||
if queryarg not in request.GET:
|
if queryarg not in request.GET:
|
||||||
q = request.GET.copy()
|
request_copy = request.GET.copy()
|
||||||
if callable(value):
|
if callable(value):
|
||||||
value = value(request)
|
value = value(request)
|
||||||
q[queryarg] = value
|
request_copy[queryarg] = value
|
||||||
request.GET = q
|
request.GET = request_copy
|
||||||
request.META['QUERY_STRING'] = request.GET.urlencode()
|
request.META['QUERY_STRING'] = request.GET.urlencode()
|
||||||
|
|
||||||
|
|
||||||
def link(*args, **kwargs):
|
def admin_link(*args, **kwargs):
|
||||||
""" utility function for creating admin links """
|
""" utility function for creating admin links """
|
||||||
field = args[0] if args else ''
|
field = args[0] if args else ''
|
||||||
order = kwargs.pop('order', field)
|
order = kwargs.pop('order', field)
|
||||||
|
@ -88,7 +88,7 @@ def link(*args, **kwargs):
|
||||||
extra = 'onclick="return showAddAnotherPopup(this);"'
|
extra = 'onclick="return showAddAnotherPopup(this);"'
|
||||||
return '<a href="%s" %s>%s</a>' % (url, extra, obj)
|
return '<a href="%s" %s>%s</a>' % (url, extra, obj)
|
||||||
display_link.allow_tags = True
|
display_link.allow_tags = True
|
||||||
display_link.short_description = _(field)
|
display_link.short_description = _(field.replace('_', ' '))
|
||||||
display_link.admin_order_field = order
|
display_link.admin_order_field = order
|
||||||
return display_link
|
return display_link
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,8 @@ class LinkHeaderRouter(DefaultRouter):
|
||||||
for _prefix, viewset, __ in self.registry:
|
for _prefix, viewset, __ in self.registry:
|
||||||
if _prefix == prefix_or_model or viewset.model == prefix_or_model:
|
if _prefix == prefix_or_model or viewset.model == prefix_or_model:
|
||||||
return viewset
|
return viewset
|
||||||
|
msg = "%s does not have a regiestered viewset" % prefix_or_model
|
||||||
|
raise KeyError(msg)
|
||||||
|
|
||||||
def insert(self, prefix_or_model, name, field, **kwargs):
|
def insert(self, prefix_or_model, name, field, **kwargs):
|
||||||
""" Dynamically add new fields to an existing serializer """
|
""" Dynamically add new fields to an existing serializer """
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import wrap_admin_view, link
|
from orchestra.admin.utils import wrap_admin_view, admin_link
|
||||||
from orchestra.core import services
|
from orchestra.core import services
|
||||||
|
|
||||||
from .filters import HasMainUserListFilter
|
from .filters import HasMainUserListFilter
|
||||||
|
@ -42,7 +42,7 @@ class AccountAdmin(ExtendedModelAdmin):
|
||||||
add_form = AccountCreationForm
|
add_form = AccountCreationForm
|
||||||
form = AccountChangeForm
|
form = AccountChangeForm
|
||||||
|
|
||||||
user_link = link('user', order='user__username')
|
user_link = admin_link('user', order='user__username')
|
||||||
|
|
||||||
def name(self, account):
|
def name(self, account):
|
||||||
return account.name
|
return account.name
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import link
|
from orchestra.admin.utils import admin_link
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
||||||
|
|
||||||
from .forms import (DatabaseUserChangeForm, DatabaseUserCreationForm,
|
from .forms import (DatabaseUserChangeForm, DatabaseUserCreationForm,
|
||||||
|
@ -21,7 +21,7 @@ class UserInline(admin.TabularInline):
|
||||||
readonly_fields = ('user_link',)
|
readonly_fields = ('user_link',)
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
user_link = link('user')
|
user_link = admin_link('user')
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
|
@ -38,7 +38,7 @@ class PermissionInline(AccountAdminMixin, admin.TabularInline):
|
||||||
extra = 0
|
extra = 0
|
||||||
filter_by_account_fields = ['database']
|
filter_by_account_fields = ['database']
|
||||||
|
|
||||||
database_link = link('database', popup=True)
|
database_link = admin_link('database', popup=True)
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.template.response import TemplateResponse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin
|
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import wrap_admin_view, link
|
from orchestra.admin.utils import wrap_admin_view, admin_link
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
from orchestra.utils import apps
|
from orchestra.utils import apps
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class DomainInline(admin.TabularInline):
|
||||||
extra = 0
|
extra = 0
|
||||||
verbose_name_plural = _("Subdomains")
|
verbose_name_plural = _("Subdomains")
|
||||||
|
|
||||||
domain_link = link()
|
domain_link = admin_link()
|
||||||
domain_link.short_description = _("Name")
|
domain_link.short_description = _("Name")
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
|
|
|
@ -12,7 +12,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
|
|
||||||
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin#, ChangeViewActions
|
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin#, ChangeViewActions
|
||||||
from orchestra.admin.utils import (link, colored, wrap_admin_view, display_timesince)
|
from orchestra.admin.utils import (admin_link, colored, wrap_admin_view,
|
||||||
|
display_timesince)
|
||||||
from orchestra.apps.contacts import settings as contacts_settings
|
from orchestra.apps.contacts import settings as contacts_settings
|
||||||
|
|
||||||
from .actions import (reject_tickets, resolve_tickets, take_tickets, close_tickets,
|
from .actions import (reject_tickets, resolve_tickets, take_tickets, close_tickets,
|
||||||
|
@ -107,8 +108,8 @@ class TicketInline(admin.TabularInline):
|
||||||
extra = 0
|
extra = 0
|
||||||
max_num = 0
|
max_num = 0
|
||||||
|
|
||||||
creator_link = link('creator')
|
creator_link = admin_link('creator')
|
||||||
owner_link = link('owner')
|
owner_link = admin_link('owner')
|
||||||
|
|
||||||
def ticket_id(self, instance):
|
def ticket_id(self, instance):
|
||||||
return '<b>%s</b>' % link()(self, instance)
|
return '<b>%s</b>' % link()(self, instance)
|
||||||
|
@ -198,9 +199,9 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeView
|
||||||
'issues/js/ticket-admin.js',
|
'issues/js/ticket-admin.js',
|
||||||
)
|
)
|
||||||
|
|
||||||
display_creator = link('creator')
|
display_creator = admin_link('creator')
|
||||||
display_queue = link('queue')
|
display_queue = admin_link('queue')
|
||||||
display_owner = link('owner')
|
display_owner = admin_link('owner')
|
||||||
|
|
||||||
def display_summary(self, ticket):
|
def display_summary(self, ticket):
|
||||||
context = {
|
context = {
|
||||||
|
@ -212,7 +213,7 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeView
|
||||||
if msg:
|
if msg:
|
||||||
context.update({
|
context.update({
|
||||||
'updated': display_timesince(msg.created_on),
|
'updated': display_timesince(msg.created_on),
|
||||||
'updater': link('author')(self, msg) if msg.author else msg.author_name,
|
'updater': admin_link('author')(self, msg) if msg.author else msg.author_name,
|
||||||
})
|
})
|
||||||
context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
|
context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
|
||||||
return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
|
return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.contrib.admin import SimpleListFilter
|
from django.contrib.admin import SimpleListFilter
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .models import Ticket
|
from .models import Ticket
|
||||||
|
|
||||||
|
@ -10,14 +11,20 @@ class MyTicketsListFilter(SimpleListFilter):
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('True', 'My Tickets'),
|
('True', _("My Tickets")),
|
||||||
('False', 'All'),
|
('False', _("All")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
if self.value() == 'True':
|
if self.value() == 'True':
|
||||||
return queryset.involved_by(request.user)
|
return queryset.involved_by(request.user)
|
||||||
|
|
||||||
|
def choices(self, cl):
|
||||||
|
""" Remove default All """
|
||||||
|
choices = iter(super(MyTicketsListFilter, self).choices(cl))
|
||||||
|
choices.next()
|
||||||
|
return choices
|
||||||
|
|
||||||
|
|
||||||
class TicketStateListFilter(SimpleListFilter):
|
class TicketStateListFilter(SimpleListFilter):
|
||||||
title = 'State'
|
title = 'State'
|
||||||
|
@ -25,14 +32,14 @@ class TicketStateListFilter(SimpleListFilter):
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('OPEN', "Open"),
|
('OPEN', _("Open")),
|
||||||
(Ticket.NEW, "New"),
|
(Ticket.NEW, _("New")),
|
||||||
(Ticket.IN_PROGRESS, "In Progress"),
|
(Ticket.IN_PROGRESS, _("In Progress")),
|
||||||
(Ticket.RESOLVED, "Resolved"),
|
(Ticket.RESOLVED, _("Resolved")),
|
||||||
(Ticket.FEEDBACK, "Feedback"),
|
(Ticket.FEEDBACK, _("Feedback")),
|
||||||
(Ticket.REJECTED, "Rejected"),
|
(Ticket.REJECTED, _("Rejected")),
|
||||||
(Ticket.CLOSED, "Closed"),
|
(Ticket.CLOSED, _("Closed")),
|
||||||
('False', 'All'),
|
('False', _("All")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
|
@ -41,3 +48,10 @@ class TicketStateListFilter(SimpleListFilter):
|
||||||
elif self.value() == 'False':
|
elif self.value() == 'False':
|
||||||
return queryset
|
return queryset
|
||||||
return queryset.filter(state=self.value())
|
return queryset.filter(state=self.value())
|
||||||
|
|
||||||
|
def choices(self, cl):
|
||||||
|
""" Remove default All """
|
||||||
|
choices = iter(super(TicketStateListFilter, self).choices(cl))
|
||||||
|
choices.next()
|
||||||
|
return choices
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.contrib.auth.admin import UserAdmin
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import link
|
from orchestra.admin.utils import admin_link
|
||||||
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
|
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
|
||||||
|
|
||||||
from .forms import ListCreationForm, ListChangeForm
|
from .forms import ListCreationForm, ListChangeForm
|
||||||
|
@ -47,7 +47,7 @@ class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
add_form = ListCreationForm
|
add_form = ListCreationForm
|
||||||
filter_by_account_fields = ['address_domain']
|
filter_by_account_fields = ['address_domain']
|
||||||
|
|
||||||
address_domain_link = link('address_domain', order='address_domain__name')
|
address_domain_link = admin_link('address_domain', order='address_domain__name')
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
useradmin = UserAdmin(List, self.admin_site)
|
useradmin = UserAdmin(List, self.admin_site)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from djcelery.humanize import naturaldate
|
from djcelery.humanize import naturaldate
|
||||||
|
|
||||||
from orchestra.admin.html import monospace_format
|
from orchestra.admin.html import monospace_format
|
||||||
from orchestra.admin.utils import link
|
from orchestra.admin.utils import admin_link
|
||||||
|
|
||||||
from .models import Server, Route, BackendLog, BackendOperation
|
from .models import Server, Route, BackendLog, BackendOperation
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class BackendOperationInline(admin.TabularInline):
|
||||||
|
|
||||||
def instance_link(self, operation):
|
def instance_link(self, operation):
|
||||||
try:
|
try:
|
||||||
return link('instance')(self, operation)
|
return admin_link('instance')(self, operation)
|
||||||
except:
|
except:
|
||||||
return _("deleted {0} {1}").format(
|
return _("deleted {0} {1}").format(
|
||||||
escape(operation.content_type), escape(operation.object_id)
|
escape(operation.content_type), escape(operation.object_id)
|
||||||
|
@ -88,11 +88,7 @@ class BackendLogAdmin(admin.ModelAdmin):
|
||||||
]
|
]
|
||||||
readonly_fields = fields
|
readonly_fields = fields
|
||||||
|
|
||||||
def server_link(self, log):
|
server_link = admin_link('server')
|
||||||
url = reverse('admin:orchestration_server_change', args=(log.server.pk,))
|
|
||||||
return '<a href="%s">%s</a>' % (url, log.server.name)
|
|
||||||
server_link.short_description = _("server")
|
|
||||||
server_link.allow_tags = True
|
|
||||||
|
|
||||||
def display_state(self, log):
|
def display_state(self, log):
|
||||||
color = STATE_COLORS.get(log.state, 'grey')
|
color = STATE_COLORS.get(log.state, 'grey')
|
||||||
|
|
|
@ -1,18 +1,29 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.db import models
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orchestra.admin import ChangeListDefaultFilter
|
||||||
|
from orchestra.admin.filters import UsedContentTypeFilter
|
||||||
|
from orchestra.admin.utils import admin_link
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
from orchestra.core import services
|
from orchestra.core import services
|
||||||
|
|
||||||
|
from .filters import ActiveOrderListFilter
|
||||||
from .models import Service, Order, MetricStorage
|
from .models import Service, Order, MetricStorage
|
||||||
|
|
||||||
|
|
||||||
class ServiceAdmin(admin.ModelAdmin):
|
class ServiceAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
'description', 'content_type', 'handler_type', 'num_orders', 'is_active'
|
||||||
|
)
|
||||||
|
list_filter = ('is_active', 'handler_type', UsedContentTypeFilter)
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'classes': ('wide',),
|
'classes': ('wide',),
|
||||||
'fields': ('description', 'content_type', 'match', 'handler', 'is_active')
|
'fields': ('description', 'content_type', 'match', 'handler_type',
|
||||||
|
'is_active')
|
||||||
}),
|
}),
|
||||||
(_("Billing options"), {
|
(_("Billing options"), {
|
||||||
'classes': ('wide',),
|
'classes': ('wide',),
|
||||||
|
@ -37,12 +48,32 @@ class ServiceAdmin(admin.ModelAdmin):
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'160'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'160'})
|
||||||
return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
|
def num_orders(self, service):
|
||||||
|
num = service.orders.count()
|
||||||
|
url = reverse('admin:orders_order_changelist')
|
||||||
|
url += '?service=%i' % service.pk
|
||||||
|
return '<a href="%s">%d</a>' % (url, num)
|
||||||
|
num_orders.short_description = _("Orders")
|
||||||
|
num_orders.admin_order_field = 'orders__count'
|
||||||
|
num_orders.allow_tags = True
|
||||||
|
|
||||||
class OrderAdmin(AccountAdminMixin, admin.ModelAdmin):
|
def get_queryset(self, request):
|
||||||
list_display = ('id', 'service', 'account_link', 'cancelled_on')
|
qs = super(ServiceAdmin, self).get_queryset(request)
|
||||||
list_filter = ('service',)
|
qs = qs.annotate(models.Count('orders'))
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class OrderAdmin(AccountAdminMixin, ChangeListDefaultFilter, admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
'id', 'service', 'account_link', 'content_object_link', 'cancelled_on'
|
||||||
|
)
|
||||||
|
list_filter = (ActiveOrderListFilter, 'service',)
|
||||||
|
default_changelist_filters = (
|
||||||
|
('is_active', 'True'),
|
||||||
|
)
|
||||||
|
|
||||||
|
content_object_link = admin_link('content_object')
|
||||||
|
|
||||||
class MetricStorageAdmin(admin.ModelAdmin):
|
class MetricStorageAdmin(admin.ModelAdmin):
|
||||||
list_display = ('order', 'value', 'created_on', 'updated_on')
|
list_display = ('order', 'value', 'created_on', 'updated_on')
|
||||||
list_filter = ('order__service',)
|
list_filter = ('order__service',)
|
||||||
|
|
28
orchestra/apps/orders/filters.py
Normal file
28
orchestra/apps/orders/filters.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from django.contrib.admin import SimpleListFilter
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class ActiveOrderListFilter(SimpleListFilter):
|
||||||
|
""" Filter tickets by created_by according to request.user """
|
||||||
|
title = 'Orders'
|
||||||
|
parameter_name = 'is_active'
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
return (
|
||||||
|
('True', _("Active")),
|
||||||
|
('False', _("Inactive")),
|
||||||
|
('None', _("All")),
|
||||||
|
)
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
if self.value() == 'True':
|
||||||
|
return queryset.active()
|
||||||
|
elif self.value() == 'False':
|
||||||
|
return queryset.inactive()
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def choices(self, cl):
|
||||||
|
""" Remove default All """
|
||||||
|
choices = iter(super(ActiveOrderListFilter, self).choices(cl))
|
||||||
|
choices.next()
|
||||||
|
return choices
|
|
@ -12,13 +12,19 @@ class ServiceHandler(plugins.Plugin):
|
||||||
def __init__(self, service):
|
def __init__(self, service):
|
||||||
self.service = service
|
self.service = service
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return getattr(self.service, attr)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_choices(cls):
|
def get_plugin_choices(cls):
|
||||||
choices = super(ServiceHandler, cls).get_plugin_choices()
|
choices = super(ServiceHandler, cls).get_plugin_choices()
|
||||||
return [('', _("Default"))] + choices
|
return [('', _("Default"))] + choices
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def get_content_type(self):
|
||||||
return getattr(self.service, attr)
|
if not self.model:
|
||||||
|
return self.content_type
|
||||||
|
app_label, model = self.model.split('.')
|
||||||
|
return ContentType.objects.get_by_natural_key(app_label, model.lower())
|
||||||
|
|
||||||
def matches(self, instance):
|
def matches(self, instance):
|
||||||
safe_locals = {
|
safe_locals = {
|
||||||
|
@ -27,13 +33,8 @@ class ServiceHandler(plugins.Plugin):
|
||||||
return eval(self.match, safe_locals)
|
return eval(self.match, safe_locals)
|
||||||
|
|
||||||
def get_metric(self, instance):
|
def get_metric(self, instance):
|
||||||
safe_locals = {
|
if self.metric:
|
||||||
instance._meta.model_name: instance
|
safe_locals = {
|
||||||
}
|
instance._meta.model_name: instance
|
||||||
return eval(self.metric, safe_locals)
|
}
|
||||||
|
return eval(self.metric, safe_locals)
|
||||||
def get_content_type(self):
|
|
||||||
if not self.model:
|
|
||||||
return self.content_type
|
|
||||||
app_label, model = self.model.split('.')
|
|
||||||
return ContentType.objects.get_by_natural_key(app_label, model.lower())
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ def search_for_related(origin, max_depth=2):
|
||||||
if hasattr(node, 'account') or isinstance(node, Account):
|
if hasattr(node, 'account') or isinstance(node, Account):
|
||||||
return node
|
return node
|
||||||
for related in related_iterator(node):
|
for related in related_iterator(node):
|
||||||
if related not in models:
|
if related and related not in models:
|
||||||
new_models = list(models)
|
new_models = list(models)
|
||||||
new_models.append(related)
|
new_models.append(related)
|
||||||
queue.append(new_models)
|
queue.append(new_models)
|
||||||
|
|
|
@ -45,8 +45,10 @@ class Service(models.Model):
|
||||||
description = models.CharField(_("description"), max_length=256, unique=True)
|
description = models.CharField(_("description"), max_length=256, unique=True)
|
||||||
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
|
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
|
||||||
match = models.CharField(_("match"), max_length=256, blank=True)
|
match = models.CharField(_("match"), max_length=256, blank=True)
|
||||||
handler = models.CharField(_("handler"), max_length=256, blank=True,
|
handler_type = models.CharField(_("handler"), max_length=256, blank=True,
|
||||||
help_text=_("Handler used to process this Service."),
|
help_text=_("Handler used for processing this Service. A handler "
|
||||||
|
"enables customized behaviour far beyond what options "
|
||||||
|
"here allow to."),
|
||||||
choices=ServiceHandler.get_plugin_choices())
|
choices=ServiceHandler.get_plugin_choices())
|
||||||
is_active = models.BooleanField(_("is active"), default=True)
|
is_active = models.BooleanField(_("is active"), default=True)
|
||||||
# Billing
|
# Billing
|
||||||
|
@ -172,14 +174,16 @@ class Service(models.Model):
|
||||||
cache.set(ct, services)
|
cache.set(ct, services)
|
||||||
return services
|
return services
|
||||||
|
|
||||||
|
# FIXME some times caching is nasty, do we really have to? make get_plugin more efficient?
|
||||||
@cached_property
|
@cached_property
|
||||||
def proxy(self):
|
def handler(self):
|
||||||
if self.handler:
|
""" Accessor of this service handler instance """
|
||||||
return ServiceHandler.get_plugin(self.handler)(self)
|
if self.handler_type:
|
||||||
|
return ServiceHandler.get_plugin(self.handler_type)(self)
|
||||||
return ServiceHandler(self)
|
return ServiceHandler(self)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
content_type = self.proxy.get_content_type()
|
content_type = self.handler.get_content_type()
|
||||||
if self.content_type != content_type:
|
if self.content_type != content_type:
|
||||||
msg =_("Content type must be equal to '%s'." % str(content_type))
|
msg =_("Content type must be equal to '%s'." % str(content_type))
|
||||||
raise ValidationError(msg)
|
raise ValidationError(msg)
|
||||||
|
@ -191,22 +195,30 @@ class Service(models.Model):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
for attr in ['matches', 'get_metric']:
|
||||||
self.proxy.matches(obj)
|
try:
|
||||||
except Exception as e:
|
getattr(self.handler, attr)(obj)
|
||||||
raise ValidationError(_(str(e)))
|
except Exception as exception:
|
||||||
|
name = type(exception).__name__
|
||||||
|
message = exception.message
|
||||||
|
msg = "{0} {1}: {2}".format(attr, name, message)
|
||||||
|
raise ValidationError(msg)
|
||||||
|
|
||||||
|
|
||||||
class OrderQuerySet(models.QuerySet):
|
class OrderQuerySet(models.QuerySet):
|
||||||
def by_object(self, obj, *args, **kwargs):
|
def by_object(self, obj, **kwargs):
|
||||||
ct = ContentType.objects.get_for_model(obj)
|
ct = ContentType.objects.get_for_model(obj)
|
||||||
return self.filter(object_id=obj.pk, content_type=ct)
|
return self.filter(object_id=obj.pk, content_type=ct, **kwargs)
|
||||||
|
|
||||||
def active(self, *args, **kwargs):
|
def active(self, **kwargs):
|
||||||
""" return active orders """
|
""" return active orders """
|
||||||
return self.filter(
|
return self.filter(
|
||||||
Q(cancelled_on__isnull=True) | Q(cancelled_on__gt=timezone.now())
|
Q(cancelled_on__isnull=True) | Q(cancelled_on__gt=timezone.now())
|
||||||
).filter(*args, **kwargs)
|
).filter(**kwargs)
|
||||||
|
|
||||||
|
def inactive(self, **kwargs):
|
||||||
|
""" return inactive orders """
|
||||||
|
return self.filter(cancelled_on__lt=timezone.now(), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Order(models.Model):
|
class Order(models.Model):
|
||||||
|
@ -234,10 +246,12 @@ class Order(models.Model):
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
instance = self.content_object
|
instance = self.content_object
|
||||||
if self.service.metric:
|
handler = self.service.handler
|
||||||
metric = self.service.get_metric(instance)
|
if handler.metric:
|
||||||
MetricStorage.store(self, metric)
|
metric = handler.get_metric(instance)
|
||||||
description = "{}: {}".format(self.service.description, str(instance))
|
if metric is not None:
|
||||||
|
MetricStorage.store(self, metric)
|
||||||
|
description = "{}: {}".format(handler.description, str(instance))
|
||||||
if self.description != description:
|
if self.description != description:
|
||||||
self.description = description
|
self.description = description
|
||||||
self.save()
|
self.save()
|
||||||
|
@ -246,7 +260,7 @@ class Order(models.Model):
|
||||||
def update_orders(cls, instance):
|
def update_orders(cls, instance):
|
||||||
for service in Service.get_services(instance):
|
for service in Service.get_services(instance):
|
||||||
orders = Order.objects.by_object(instance, service=service).active()
|
orders = Order.objects.by_object(instance, service=service).active()
|
||||||
if service.matches(instance):
|
if service.handler.matches(instance):
|
||||||
if not orders:
|
if not orders:
|
||||||
account_id = getattr(instance, 'account_id', instance.pk)
|
account_id = getattr(instance, 'account_id', instance.pk)
|
||||||
order = cls.objects.create(content_object=instance,
|
order = cls.objects.create(content_object=instance,
|
||||||
|
@ -289,23 +303,20 @@ class MetricStorage(models.Model):
|
||||||
|
|
||||||
@receiver(pre_delete, dispatch_uid="orders.cancel_orders")
|
@receiver(pre_delete, dispatch_uid="orders.cancel_orders")
|
||||||
def cancel_orders(sender, **kwargs):
|
def cancel_orders(sender, **kwargs):
|
||||||
if (not sender in [MetricStorage, LogEntry, Order, Service] and
|
if sender not in [MetricStorage, LogEntry, Order, Service]:
|
||||||
not Service in sender.__mro__):
|
instance = kwargs['instance']
|
||||||
instance = kwargs['instance']
|
for order in Order.objects.by_object(instance).active():
|
||||||
for order in Order.objects.by_object(instance).active():
|
order.cancel()
|
||||||
order.cancel()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, dispatch_uid="orders.update_orders")
|
@receiver(post_save, dispatch_uid="orders.update_orders")
|
||||||
@receiver(post_delete, dispatch_uid="orders.update_orders")
|
@receiver(post_delete, dispatch_uid="orders.update_orders")
|
||||||
def update_orders(sender, **kwargs):
|
def update_orders(sender, **kwargs):
|
||||||
if (not sender in [MetricStorage, LogEntry, Order, Service] and
|
if sender not in [MetricStorage, LogEntry, Order, Service]:
|
||||||
not Service in sender.__mro__):
|
instance = kwargs['instance']
|
||||||
instance = kwargs['instance']
|
if instance.pk:
|
||||||
print kwargs
|
# post_save
|
||||||
if instance.pk:
|
Order.update_orders(instance)
|
||||||
# post_save
|
related = search_for_related(instance)
|
||||||
Order.update_orders(instance)
|
if related:
|
||||||
related = search_for_related(instance)
|
Order.update_orders(related)
|
||||||
if related:
|
|
||||||
Order.update_orders(related)
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from djcelery.humanize import naturaldate
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.filters import UsedContentTypeFilter
|
from orchestra.admin.filters import UsedContentTypeFilter
|
||||||
from orchestra.admin.utils import insertattr, get_modeladmin, link
|
from orchestra.admin.utils import insertattr, get_modeladmin, admin_link
|
||||||
from orchestra.core import services
|
from orchestra.core import services
|
||||||
from orchestra.utils import running_syncdb
|
from orchestra.utils import running_syncdb
|
||||||
|
|
||||||
|
@ -71,10 +71,7 @@ class ResourceDataAdmin(admin.ModelAdmin):
|
||||||
list_filter = ('resource',)
|
list_filter = ('resource',)
|
||||||
readonly_fields = ('content_object_link',)
|
readonly_fields = ('content_object_link',)
|
||||||
|
|
||||||
def content_object_link(self, data):
|
content_object_link = admin_link('content_object')
|
||||||
return link('content_object')(self, data)
|
|
||||||
content_object_link.allow_tags = True
|
|
||||||
content_object_link.short_description = _("Content object")
|
|
||||||
|
|
||||||
|
|
||||||
class MonitorDataAdmin(admin.ModelAdmin):
|
class MonitorDataAdmin(admin.ModelAdmin):
|
||||||
|
@ -82,10 +79,7 @@ class MonitorDataAdmin(admin.ModelAdmin):
|
||||||
list_filter = ('monitor',)
|
list_filter = ('monitor',)
|
||||||
readonly_fields = ('content_object_link',)
|
readonly_fields = ('content_object_link',)
|
||||||
|
|
||||||
def content_object_link(self, data):
|
content_object_link = admin_link('content_object')
|
||||||
return link('content_object')(self, data)
|
|
||||||
content_object_link.allow_tags = True
|
|
||||||
content_object_link.short_description = _("Content object")
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Resource, ResourceAdmin)
|
admin.site.register(Resource, ResourceAdmin)
|
||||||
|
|
|
@ -164,7 +164,15 @@ def create_resource_relation():
|
||||||
class ResourceHandler(object):
|
class ResourceHandler(object):
|
||||||
""" account.resources.web """
|
""" account.resources.web """
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
return self.obj.resource_set.get(resource__name=attr)
|
""" get or create ResourceData """
|
||||||
|
try:
|
||||||
|
return self.obj.resource_set.get(resource__name=attr)
|
||||||
|
except ResourceData.DoesNotExist:
|
||||||
|
model = self.obj._meta.model_name
|
||||||
|
resource = Resource.objects.get(content_type__model=model,
|
||||||
|
name=attr, is_active=True)
|
||||||
|
return ResourceData.objects.create(content_object=self.obj,
|
||||||
|
resource=resource)
|
||||||
|
|
||||||
def __get__(self, obj, cls):
|
def __get__(self, obj, cls):
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
|
|
@ -27,7 +27,10 @@ if not running_syncdb():
|
||||||
# TODO why this is even loaded during syncdb?
|
# TODO why this is even loaded during syncdb?
|
||||||
for resources in Resource.group_by_content_type():
|
for resources in Resource.group_by_content_type():
|
||||||
model = resources[0].content_type.model_class()
|
model = resources[0].content_type.model_class()
|
||||||
router.insert(model, 'resources', ResourceSerializer, required=False, many=True)
|
try:
|
||||||
|
router.insert(model, 'resources', ResourceSerializer, required=False, many=True)
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
def validate_resources(self, attrs, source, _resources=resources):
|
def validate_resources(self, attrs, source, _resources=resources):
|
||||||
""" Creates missing resources """
|
""" Creates missing resources """
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import insertattr, link
|
from orchestra.admin.utils import insertattr, admin_link
|
||||||
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
|
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
|
||||||
from orchestra.apps.domains.forms import DomainIterator
|
from orchestra.apps.domains.forms import DomainIterator
|
||||||
from orchestra.apps.users.roles.admin import RoleAdmin
|
from orchestra.apps.users.roles.admin import RoleAdmin
|
||||||
|
@ -79,7 +79,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
filter_by_account_fields = ['domain']
|
filter_by_account_fields = ['domain']
|
||||||
filter_horizontal = ['mailboxes']
|
filter_horizontal = ['mailboxes']
|
||||||
|
|
||||||
domain_link = link('domain', order='domain__name')
|
domain_link = admin_link('domain', order='domain__name')
|
||||||
|
|
||||||
def email_link(self, address):
|
def email_link(self, address):
|
||||||
link = self.domain_link(address)
|
link = self.domain_link(address)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import link
|
from orchestra.admin.utils import admin_link
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
||||||
from orchestra.apps.accounts.widgets import account_related_field_widget_factory
|
from orchestra.apps.accounts.widgets import account_related_field_widget_factory
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class ContentInline(AccountAdminMixin, admin.TabularInline):
|
||||||
readonly_fields = ('webapp_link', 'webapp_type')
|
readonly_fields = ('webapp_link', 'webapp_type')
|
||||||
filter_by_account_fields = ['webapp']
|
filter_by_account_fields = ['webapp']
|
||||||
|
|
||||||
webapp_link = link('webapp', popup=True)
|
webapp_link = admin_link('webapp', popup=True)
|
||||||
webapp_link.short_description = _("Web App")
|
webapp_link.short_description = _("Web App")
|
||||||
|
|
||||||
def webapp_type(self, content):
|
def webapp_type(self, content):
|
||||||
|
|
Loading…
Reference in a new issue