Refactor admin_fields

This commit is contained in:
Marc 2014-07-24 09:53:34 +00:00
parent 8c13e75d5d
commit 06db4cd346
14 changed files with 119 additions and 140 deletions

View file

@ -65,3 +65,5 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
* Be consistent with dates: name_on, created ?
* backend logs with hal logo

View file

@ -1,4 +1,4 @@
from functools import wraps
from functools import wraps, partial
from django.contrib import messages
from django.contrib.admin import helpers
@ -7,6 +7,22 @@ from django.utils.decorators import available_attrs
from django.utils.encoding import force_text
def admin_field(method):
def admin_field_wrapper(*args, **kwargs):
""" utility function for creating admin links """
kwargs['field'] = args[0] if args else ''
kwargs['order'] = kwargs.get('order', kwargs['field'])
kwargs['popup'] = kwargs.get('popup', False)
kwargs['description'] = kwargs.get('description',
kwargs['field'].split('__')[-1].replace('_', ' ').capitalize())
admin_method = partial(method, **kwargs)
admin_method.short_description = kwargs['description']
admin_method.allow_tags = True
admin_method.admin_order_field = kwargs['order']
return admin_method
return admin_field_wrapper
def action_with_confirmation(action_name, extra_context={},
template='admin/orchestra/generic_confirmation.html'):
"""
@ -14,7 +30,6 @@ def action_with_confirmation(action_name, extra_context={},
If custom template is provided the form must contain:
<input type="hidden" name="post" value="generic_confirmation" />
"""
def decorator(func, extra_context=extra_context, template=template):
@wraps(func, assigned=available_attrs(func))
def inner(modeladmin, request, queryset):
@ -23,16 +38,16 @@ def action_with_confirmation(action_name, extra_context={},
stay = func(modeladmin, request, queryset)
if not stay:
return
opts = modeladmin.model._meta
app_label = opts.app_label
action_value = func.__name__
if len(queryset) == 1:
objects_name = force_text(opts.verbose_name)
else:
objects_name = force_text(opts.verbose_name_plural)
context = {
"title": "Are you sure?",
"content_message": "Are you sure you want to %s the selected %s?" %
@ -45,12 +60,11 @@ def action_with_confirmation(action_name, extra_context={},
"app_label": app_label,
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
}
context.update(extra_context)
# Display the confirmation page
return TemplateResponse(request, template,
context, current_app=modeladmin.admin_site.name)
return inner
return decorator

View file

@ -32,7 +32,8 @@ def get_services():
for model, options in services.get().iteritems():
if options.get('menu', True):
opts = model._meta
url = reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name))
url = reverse('admin:{}_{}_changelist'.format(
opts.app_label, opts.model_name))
name = capfirst(options.get('verbose_name_plural'))
result.append(items.MenuItem(name, url))
return sorted(result, key=lambda i: i.title)
@ -40,24 +41,27 @@ def get_services():
def get_account_items():
childrens = [
items.MenuItem(_("Accounts"), reverse('admin:accounts_account_changelist'))
items.MenuItem(_("Accounts"),
reverse('admin:accounts_account_changelist'))
]
if isinstalled('orchestra.apps.contacts'):
url = reverse('admin:contacts_contact_changelist')
childrens.append(items.MenuItem(_("Contacts"), url))
if isinstalled('orchestra.apps.users'):
url = reverse('admin:users_user_changelist')
users = [items.MenuItem(_("Users"), url)]
if isinstalled('rest_framework.authtoken'):
tokens = reverse('admin:authtoken_token_changelist')
users.append(items.MenuItem(_("Tokens"), tokens))
childrens.append(items.MenuItem(_("Users"), url, children=users))
childrens.append(items.MenuItem(_("Users"), url))
if isinstalled('orchestra.apps.prices'):
url = reverse('admin:prices_pack_changelist')
childrens.append(items.MenuItem(_("Packs"), url))
if isinstalled('orchestra.apps.orders'):
url = reverse('admin:orders_order_changelist')
childrens.append(items.MenuItem(_("Orders"), url))
if isinstalled('orchestra.apps.bills'):
url = reverse('admin:bills_bill_changelist')
childrens.append(items.MenuItem(_("Bills"), url))
if isinstalled('orchestra.apps.payments'):
url = reverse('admin:payments_transaction_changelist')
childrens.append(items.MenuItem(_("Transactions"), url))
if isinstalled('orchestra.apps.issues'):
url = reverse('admin:issues_ticket_changelist')
childrens.append(items.MenuItem(_("Tickets"), url))
@ -92,7 +96,7 @@ def get_administration_items():
childrens.append(items.MenuItem(_("Miscellaneous"), url))
if isinstalled('orchestra.apps.issues'):
url = reverse('admin:issues_queue_changelist')
childrens.append(items.MenuItem(_("Issue queues"), url))
childrens.append(items.MenuItem(_("Ticket queues"), url))
if isinstalled('djcelery'):
task = reverse('admin:djcelery_taskstate_changelist')
periodic = reverse('admin:djcelery_periodictask_changelist')

View file

@ -12,6 +12,8 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.models.utils import get_field_value
from orchestra.utils.humanize import naturaldate
from .decorators import admin_field
def get_modeladmin(model, import_module=True):
""" returns the modeladmin registred for model """
@ -44,7 +46,9 @@ def insertattr(model, name, value, weight=0):
weights = {}
if hasattr(modeladmin, 'weights') and name in modeladmin.weights:
weights = modeladmin.weights.get(name)
inserted_attrs[name] = [ (attr, weights.get(attr, 0)) for attr in getattr(modeladmin, name) ]
inserted_attrs[name] = [
(attr, weights.get(attr, 0)) for attr in getattr(modeladmin, name)
]
inserted_attrs[name].append((value, weight))
inserted_attrs[name].sort(key=lambda a: a[1])
@ -70,85 +74,40 @@ def set_default_filter(queryarg, request, value):
request.META['QUERY_STRING'] = request.GET.urlencode()
@admin_field
def admin_link(*args, **kwargs):
""" utility function for creating admin links """
field = args[0] if args else ''
order = kwargs.pop('order', field)
popup = kwargs.pop('popup', False)
def display_link(*args):
instance = args[-1]
obj = getattr(instance, field, instance)
if not getattr(obj, 'pk', None):
return '---'
opts = obj._meta
view_name = 'admin:%s_%s_change' % (opts.app_label, opts.model_name)
url = reverse(view_name, args=(obj.pk,))
extra = ''
if popup:
extra = 'onclick="return showAddAnotherPopup(this);"'
return '<a href="%s" %s>%s</a>' % (url, extra, obj)
display_link.allow_tags = True
display_link.short_description = _(field.replace('_', ' '))
display_link.admin_order_field = order
return display_link
instance = args[-1]
obj = get_field_value(instance, kwargs['field'])
if not getattr(obj, 'pk', None):
return '---'
opts = obj._meta
view_name = 'admin:%s_%s_change' % (opts.app_label, opts.model_name)
url = reverse(view_name, args=(obj.pk,))
extra = ''
if kwargs['popup']:
extra = 'onclick="return showAddAnotherPopup(this);"'
return '<a href="%s" %s>%s</a>' % (url, extra, obj)
def colored(field_name, colours, description='', verbose=False, bold=True):
""" returns a method that will render obj with colored html """
def colored_field(obj, field=field_name, colors=colours, verbose=verbose):
value = escape(get_field_value(obj, field))
color = colors.get(value, "black")
if verbose:
# Get the human-readable value of a choice field
value = getattr(obj, 'get_%s_display' % field)()
colored_value = '<span style="color: %s;">%s</span>' % (color, value)
if bold:
colored_value = '<b>%s</b>' % colored_value
return mark_safe(colored_value)
if not description:
description = field_name.split('__').pop().replace('_', ' ').capitalize()
colored_field.short_description = description
colored_field.allow_tags = True
colored_field.admin_order_field = field_name
return colored_field
@admin_field
def admin_colored(*args, **kwargs):
instance = args[-1]
field = kwargs['field']
value = escape(get_field_value(instance, field))
color = kwargs.get('colors', {}).get(value, 'black')
value = getattr(instance, 'get_%s_display' % field)().upper()
colored_value = '<span style="color: %s;">%s</span>' % (color, value)
if kwargs.get('bold', True):
colored_value = '<b>%s</b>' % colored_value
return mark_safe(colored_value)
#def display_timesince(date, double=False):
# """
# Format date for messages create_on: show a relative time
# with contextual helper to show fulltime format.
# """
# if not date:
# return 'Never'
# date_rel = timesince(date)
# if not double:
# date_rel = date_rel.split(',')[0]
# date_rel += ' ago'
# date_abs = date.strftime("%Y-%m-%d %H:%M:%S %Z")
# return mark_safe("<span title='%s'>%s</span>" % (date_abs, date_rel))
def admin_date(field, **kwargs):
""" utility function for creating admin dates """
default = kwargs.pop('default', '')
order = kwargs.pop('order', field)
def display_date(*args):
instance = args[-1]
value = get_field_value(instance, field)
if not value:
return default
return '<span title="{0}">{1}</span>'.format(
escape(str(value)), escape(naturaldate(value)),
)
display_date.short_description = _(field.replace('_', ' '))
display_date.admin_order_field = order
display_date.allow_tags = True
return display_date
#def display_timeuntil(date):
# date_rel = timeuntil(date) + ' left'
# date_abs = date.strftime("%Y-%m-%d %H:%M:%S %Z")
# return mark_safe("<span title='%s'>%s</span>" % (date_abs, date_rel))
@admin_field
def admin_date(*args, **kwargs):
instance = args[-1]
value = get_field_value(instance, kwargs['field'])
if not value:
return kwargs.get('default', '')
return '<span title="{0}">{1}</span>'.format(
escape(str(value)), escape(naturaldate(value)),
)

View file

@ -12,7 +12,8 @@ from django.utils.translation import ugettext_lazy as _
from markdown import markdown
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin#, ChangeViewActions
from orchestra.admin.utils import admin_link, colored, wrap_admin_view, admin_date
from orchestra.admin.utils import (admin_link, admin_colored, wrap_admin_view,
admin_date)
from orchestra.apps.contacts import settings as contacts_settings
from .actions import (reject_tickets, resolve_tickets, take_tickets, close_tickets,
@ -111,19 +112,13 @@ class TicketInline(admin.TabularInline):
owner_link = admin_link('owner')
created = admin_link('created_on')
last_modified = admin_link('last_modified_on')
colored_state = admin_colored('state', colors=STATE_COLORS, bold=False)
colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
def ticket_id(self, instance):
return '<b>%s</b>' % admin_link()(instance)
ticket_id.short_description = '#'
ticket_id.allow_tags = True
def colored_state(self, instance):
return colored('state', STATE_COLORS, bold=False)(instance)
colored_state.short_description = _("State")
def colored_priority(self, instance):
return colored('priority', PRIORITY_COLORS, bold=False)(instance)
colored_priority.short_description = _("Priority")
class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeViewActions,
@ -198,6 +193,8 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeView
display_queue = admin_link('queue')
display_owner = admin_link('owner')
last_modified = admin_date('last_modified_on')
display_state = admin_colored('state', colors=STATE_COLORS, bold=False)
display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
def display_summary(self, ticket):
context = {
@ -216,18 +213,6 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeView
display_summary.short_description = 'Summary'
display_summary.allow_tags = True
def display_priority(self, ticket):
""" State colored for change_form """
return colored('priority', PRIORITY_COLORS, bold=False, verbose=True)(ticket)
display_priority.short_description = _("Priority")
display_priority.admin_order_field = 'priority'
def display_state(self, ticket):
""" State colored for change_form """
return colored('state', STATE_COLORS, bold=False, verbose=True)(ticket)
display_state.short_description = _("State")
display_state.admin_order_field = 'state'
def unbold_id(self, ticket):
""" Unbold id if ticket is read """
if ticket.is_read_by(self.user):

View file

@ -4,7 +4,7 @@ from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from orchestra.admin.html import monospace_format
from orchestra.admin.utils import admin_link, admin_date, colored
from orchestra.admin.utils import admin_link, admin_date, admin_colored
from .models import Server, Route, BackendLog, BackendOperation
@ -90,7 +90,7 @@ class BackendLogAdmin(admin.ModelAdmin):
server_link = admin_link('server')
display_last_update = admin_date('last_update')
display_created = admin_date('created')
display_state = colored('state', STATE_COLORS)
display_state = admin_colored('state', colors=STATE_COLORS)
def mono_script(self, log):
return monospace_format(escape(log.script))

View file

@ -1,7 +1,30 @@
from django.contrib import admin
from orchestra.admin.utils import admin_colored, admin_link
from .models import PaymentSource, Transaction
STATE_COLORS = {
Transaction.WAITTING_PROCESSING: 'darkorange',
Transaction.WAITTING_CONFIRMATION: 'orange',
Transaction.CONFIRMED: 'green',
Transaction.REJECTED: 'red',
Transaction.LOCKED: 'magenta',
Transaction.DISCARTED: 'blue',
}
class TransactionAdmin(admin.ModelAdmin):
list_display = (
'id', 'bill_link', 'account_link', 'method', 'display_state', 'amount'
)
list_filter = ('method', 'state')
bill_link = admin_link('bill')
account_link = admin_link('bill__account')
display_state = admin_colored('state', colors=STATE_COLORS)
admin.site.register(PaymentSource)
admin.site.register(Transaction)
admin.site.register(Transaction, TransactionAdmin)

View file

@ -12,6 +12,7 @@ class PaymentSource(models.Model):
method = models.CharField(_("method"), max_length=32,
choices=PaymentMethod.get_plugin_choices())
data = JSONField(_("data"))
is_active = models.BooleanField(_("is active"), default=True)
class Transaction(models.Model):
@ -22,14 +23,15 @@ class Transaction(models.Model):
LOCKED = 'LOCKED'
DISCARTED = 'DISCARTED'
STATES = (
(WAITTING_PROCESSING, _("Waitting for processing")),
(WAITTING_CONFIRMATION, _("Waitting for confirmation")),
(WAITTING_PROCESSING, _("Waitting processing")),
(WAITTING_CONFIRMATION, _("Waitting confirmation")),
(CONFIRMED, _("Confirmed")),
(REJECTED, _("Rejected")),
(LOCKED, _("Locked")),
(DISCARTED, _("Discarted")),
)
# TODO account fk?
bill = models.ForeignKey('bills.bill', verbose_name=_("bill"),
related_name='transactions')
method = models.CharField(_("payment method"), max_length=32,
@ -42,3 +44,6 @@ class Transaction(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
modified_on = models.DateTimeField(auto_now=True)
related = models.ForeignKey('self', null=True, blank=True)
def __unicode__(self):
return "Transaction {}".format(self.id)

View file

@ -15,8 +15,8 @@ from .models import Resource, ResourceData, MonitorData
class ResourceAdmin(ExtendedModelAdmin):
list_display = (
'id', 'name', 'verbose_name', 'content_type', 'period', 'ondemand',
'default_allocation', 'disable_trigger', 'crontab',
'id', 'verbose_name', 'content_type', 'period', 'ondemand',
'default_allocation', 'unit', 'disable_trigger', 'crontab',
)
list_filter = (UsedContentTypeFilter, 'period', 'ondemand', 'disable_trigger')
fieldsets = (

View file

@ -179,7 +179,7 @@ FLUENT_DASHBOARD_APP_ICONS = {
'miscellaneous/miscellaneous': 'applications-other.png',
# Accounts
'accounts/account': 'Face-monkey.png',
'contacts/contact': 'contact.png',
'contacts/contact': 'contact_book.png',
'orders/order': 'basket.png',
'orders/service': 'price.png',
'prices/pack': 'Pack.png',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -15,8 +15,8 @@
width="48"
version="1.0"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="TuxBox.svg"
inkscape:export-filename="/home/glic3rinu/orchestra/django-orchestra/orchestra/static/orchestra/icons/Pack.png"
sodipodi:docname="Pack.svg"
inkscape:export-filename="/home/glic3/orchestra/django-orchestra/orchestra/static/orchestra/icons/Pack.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<svg:metadata
@ -45,7 +45,7 @@
id="namedview50"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="75.979939"
inkscape:cx="75.97994"
inkscape:cy="-22.905943"
inkscape:window-x="0"
inkscape:window-y="27"
@ -3780,19 +3780,6 @@
y1="7.9757"
x2="15.464"
y2="45.042" />
<svg:filter
color-interpolation-filters="sRGB"
id="filter3974-4"
height="2.5622675"
y="-0.78113377"
width="1.1420243"
x="-0.071012162"
inkscape:collect="always">
<svg:feGaussianBlur
id="feGaussianBlur3976-3"
stdDeviation="0.97641723"
inkscape:collect="always" />
</svg:filter>
<svg:linearGradient
gradientTransform="translate(0.01860128,-4.0163098)"
gradientUnits="userSpaceOnUse"
@ -4294,7 +4281,7 @@
id="path3273" />
<svg:g
id="g3888"
transform="matrix(0.7674386,0,0,0.7674386,-7.6813433,9.7817236)">
transform="matrix(0.67441404,0,0,0.67441404,-1.7640493,12.799498)">
<svg:g
transform="matrix(0.91489252,0,0,0.91489252,30.720532,5.6526667)"
id="layer2">

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB