Added resource history and domain record massive edditing
This commit is contained in:
parent
63c9f381bc
commit
0d9058d266
7
TODO.md
7
TODO.md
|
@ -423,3 +423,10 @@ Colaesce('total', 'computed_total')
|
||||||
Case
|
Case
|
||||||
|
|
||||||
# case on payment transaction state ? case when trans.amount >
|
# case on payment transaction state ? case when trans.amount >
|
||||||
|
|
||||||
|
# bill changelist: dates: (closed_on, created_on, updated_on)
|
||||||
|
|
||||||
|
|
||||||
|
# Add record modeladmin action: select domains + add records (formset) to selected domains
|
||||||
|
|
||||||
|
# Resource data inline show info: link to monitor data, and history chart: link to monitor data of each item
|
||||||
|
|
|
@ -47,7 +47,8 @@ class AdminFormSet(BaseModelFormSet):
|
||||||
|
|
||||||
|
|
||||||
def adminmodelformset_factory(modeladmin, form, formset=AdminFormSet, **kwargs):
|
def adminmodelformset_factory(modeladmin, form, formset=AdminFormSet, **kwargs):
|
||||||
formset = modelformset_factory(modeladmin.model, form=form, formset=formset, **kwargs)
|
model = kwargs.pop('model', modeladmin.model)
|
||||||
|
formset = modelformset_factory(model, form=form, formset=formset, **kwargs)
|
||||||
formset.modeladmin = modeladmin
|
formset.modeladmin = modeladmin
|
||||||
return formset
|
return formset
|
||||||
|
|
||||||
|
|
|
@ -200,10 +200,6 @@ class Bill(models.Model):
|
||||||
errors['amend_of'] = _("Related invoice is an amendment.")
|
errors['amend_of'] = _("Related invoice is an amendment.")
|
||||||
if errors:
|
if errors:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
elif self.type in self.AMEND_MAP.values():
|
|
||||||
raise ValidationError({
|
|
||||||
'amend_of': _("Type %s requires an amend of link.") % self.get_type_display()
|
|
||||||
})
|
|
||||||
|
|
||||||
def get_payment_state_display(self):
|
def get_payment_state_display(self):
|
||||||
value = self.payment_state
|
value = self.payment_state
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from django.contrib.admin import helpers
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
from orchestra.admin.forms import adminmodelformset_factory
|
||||||
|
from orchestra.admin.utils import get_object_from_url, change_url
|
||||||
|
from orchestra.utils.python import AttrDict
|
||||||
|
|
||||||
|
from .forms import RecordForm, RecordEditFormSet
|
||||||
|
from .models import Record
|
||||||
|
|
||||||
|
|
||||||
def view_zone(modeladmin, request, queryset):
|
def view_zone(modeladmin, request, queryset):
|
||||||
|
@ -12,3 +24,68 @@ def view_zone(modeladmin, request, queryset):
|
||||||
return TemplateResponse(request, 'admin/domains/domain/view_zone.html', context)
|
return TemplateResponse(request, 'admin/domains/domain/view_zone.html', context)
|
||||||
view_zone.url_name = 'view-zone'
|
view_zone.url_name = 'view-zone'
|
||||||
view_zone.verbose_name = _("View zone")
|
view_zone.verbose_name = _("View zone")
|
||||||
|
|
||||||
|
|
||||||
|
def edit_records(modeladmin, request, queryset):
|
||||||
|
formsets = []
|
||||||
|
for domain in queryset.prefetch_related('records'):
|
||||||
|
modeladmin_copy = copy.copy(modeladmin)
|
||||||
|
modeladmin_copy.model = Record
|
||||||
|
link = '<a href="%s">%s</a>' % (change_url(domain), domain.name)
|
||||||
|
modeladmin_copy.verbose_name_plural = mark_safe(link)
|
||||||
|
RecordFormSet = adminmodelformset_factory(
|
||||||
|
modeladmin_copy, RecordForm, formset=RecordEditFormSet, extra=1, can_delete=True)
|
||||||
|
formset = RecordFormSet(queryset=domain.records.all(), prefix=domain.id)
|
||||||
|
formset.instance = domain
|
||||||
|
formset.cls = RecordFormSet
|
||||||
|
formsets.append(formset)
|
||||||
|
|
||||||
|
if request.POST.get('post') == 'generic_confirmation':
|
||||||
|
posted_formsets = []
|
||||||
|
all_valid = True
|
||||||
|
for formset in formsets:
|
||||||
|
instance = formset.instance
|
||||||
|
formset = formset.cls(
|
||||||
|
request.POST, request.FILES, queryset=formset.queryset, prefix=instance.id)
|
||||||
|
formset.instance = instance
|
||||||
|
if not formset.is_valid():
|
||||||
|
all_valid = False
|
||||||
|
posted_formsets.append(formset)
|
||||||
|
formsets = posted_formsets
|
||||||
|
if all_valid:
|
||||||
|
for formset in formsets:
|
||||||
|
for form in formset.forms:
|
||||||
|
form.instance.domain_id = formset.instance.id
|
||||||
|
formset.save()
|
||||||
|
fake_form = AttrDict({
|
||||||
|
'changed_data': False
|
||||||
|
})
|
||||||
|
change_message = modeladmin.construct_change_message(request, fake_form, [formset])
|
||||||
|
modeladmin.log_change(request, formset.instance, change_message)
|
||||||
|
num = len(formsets)
|
||||||
|
message = ungettext(
|
||||||
|
_("Records for one selected domain have been updated."),
|
||||||
|
_("Records for %i selected domains have been updated.") % num,
|
||||||
|
num)
|
||||||
|
modeladmin.message_user(request, message)
|
||||||
|
return
|
||||||
|
|
||||||
|
opts = modeladmin.model._meta
|
||||||
|
context = {
|
||||||
|
'title': _("Edit records"),
|
||||||
|
'action_name': 'Edit records',
|
||||||
|
'action_value': 'edit_records',
|
||||||
|
'display_objects': [],
|
||||||
|
'queryset': queryset,
|
||||||
|
'opts': opts,
|
||||||
|
'app_label': opts.app_label,
|
||||||
|
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
|
||||||
|
'formsets': formsets,
|
||||||
|
'obj': get_object_from_url(modeladmin, request),
|
||||||
|
}
|
||||||
|
return render(request, 'admin/domains/domain/edit_records.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
def add_records(modeladmin, request, queryset):
|
||||||
|
# TODO
|
||||||
|
pass
|
||||||
|
|
|
@ -8,24 +8,17 @@ from orchestra.admin.utils import admin_link, change_url
|
||||||
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
||||||
from orchestra.utils import apps
|
from orchestra.utils import apps
|
||||||
|
|
||||||
from .actions import view_zone
|
from .actions import view_zone, edit_records
|
||||||
from .filters import TopDomainListFilter
|
from .filters import TopDomainListFilter
|
||||||
from .forms import RecordInlineFormSet, BatchDomainCreationAdminForm
|
from .forms import RecordForm, RecordInlineFormSet, BatchDomainCreationAdminForm
|
||||||
from .models import Domain, Record
|
from .models import Domain, Record
|
||||||
|
|
||||||
|
|
||||||
class RecordInline(admin.TabularInline):
|
class RecordInline(admin.TabularInline):
|
||||||
model = Record
|
model = Record
|
||||||
|
form = RecordForm
|
||||||
formset = RecordInlineFormSet
|
formset = RecordInlineFormSet
|
||||||
verbose_name_plural = _("Extra records")
|
verbose_name_plural = _("Extra records")
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
|
||||||
""" Make value input widget bigger """
|
|
||||||
if db_field.name == 'value':
|
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
|
||||||
if db_field.name == 'ttl':
|
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'10'})
|
|
||||||
return super(RecordInline, self).formfield_for_dbfield(db_field, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class DomainInline(admin.TabularInline):
|
class DomainInline(admin.TabularInline):
|
||||||
|
@ -63,6 +56,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
change_readonly_fields = ('name', 'serial')
|
change_readonly_fields = ('name', 'serial')
|
||||||
search_fields = ('name', 'account__username')
|
search_fields = ('name', 'account__username')
|
||||||
add_form = BatchDomainCreationAdminForm
|
add_form = BatchDomainCreationAdminForm
|
||||||
|
actions = (edit_records,)
|
||||||
change_view_actions = [view_zone]
|
change_view_actions = [view_zone]
|
||||||
|
|
||||||
def structured_name(self, domain):
|
def structured_name(self, domain):
|
||||||
|
|
|
@ -2,6 +2,8 @@ from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orchestra.admin.forms import AdminFormSet
|
||||||
|
|
||||||
from . import validators
|
from . import validators
|
||||||
from .helpers import domain_for_validation
|
from .helpers import domain_for_validation
|
||||||
from .models import Domain
|
from .models import Domain
|
||||||
|
@ -63,17 +65,35 @@ class BatchDomainCreationAdminForm(forms.ModelForm):
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class RecordInlineFormSet(forms.models.BaseInlineFormSet):
|
class RecordForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
fields = ('ttl', 'type', 'value')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(RecordForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['ttl'].widget = forms.TextInput(attrs={'size':'10'})
|
||||||
|
self.fields['value'].widget = forms.TextInput(attrs={'size':'100'})
|
||||||
|
|
||||||
|
|
||||||
|
class ValidateZoneMixin(object):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
""" Checks if everything is consistent """
|
""" Checks if everything is consistent """
|
||||||
super(RecordInlineFormSet, self).clean()
|
super(ValidateZoneMixin, self).clean()
|
||||||
if any(self.errors):
|
if any(formset.errors):
|
||||||
return
|
return
|
||||||
if self.instance.name:
|
if formset.instance.name:
|
||||||
records = []
|
records = []
|
||||||
for form in self.forms:
|
for form in formset.forms:
|
||||||
data = form.cleaned_data
|
data = form.cleaned_data
|
||||||
if data and not data['DELETE']:
|
if data and not data['DELETE']:
|
||||||
records.append(data)
|
records.append(data)
|
||||||
domain = domain_for_validation(self.instance, records)
|
domain = domain_for_validation(formset.instance, records)
|
||||||
validators.validate_zone(domain.render_zone())
|
validators.validate_zone(domain.render_zone())
|
||||||
|
|
||||||
|
|
||||||
|
class RecordEditFormSet(ValidateZoneMixin, AdminFormSet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RecordInlineFormSet(ValidateZoneMixin, forms.models.BaseInlineFormSet):
|
||||||
|
pass
|
||||||
|
|
|
@ -20,7 +20,7 @@ class Domain(models.Model):
|
||||||
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set',
|
top = models.ForeignKey('domains.Domain', null=True, related_name='subdomain_set',
|
||||||
editable=False)
|
editable=False)
|
||||||
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, editable=False,
|
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, editable=False,
|
||||||
help_text=_("A revision number that changes whenever you update your domain."))
|
help_text=_("A revision number that changes whenever this domain is updated."))
|
||||||
refresh = models.IntegerField(_("refresh"), null=True, blank=True,
|
refresh = models.IntegerField(_("refresh"), null=True, blank=True,
|
||||||
validators=[validators.validate_zone_interval],
|
validators=[validators.validate_zone_interval],
|
||||||
help_text=_("The time a secondary DNS server waits before querying the primary DNS "
|
help_text=_("The time a secondary DNS server waits before querying the primary DNS "
|
||||||
|
@ -182,10 +182,10 @@ class Domain(models.Model):
|
||||||
"%s." % settings.DOMAINS_DEFAULT_NAME_SERVER,
|
"%s." % settings.DOMAINS_DEFAULT_NAME_SERVER,
|
||||||
utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER),
|
utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER),
|
||||||
str(self.serial),
|
str(self.serial),
|
||||||
settings.DOMAINS_DEFAULT_REFRESH if self.refresh is None else self.refresh,
|
self.refresh or settings.DOMAINS_DEFAULT_REFRESH,
|
||||||
settings.DOMAINS_DEFAULT_RETRY if self.retry is None else self.retry,
|
self.retry or settings.DOMAINS_DEFAULT_RETRY,
|
||||||
settings.DOMAINS_DEFAULT_EXPIRE if self.expire is None else self.expire,
|
self.expire or settings.DOMAINS_DEFAULT_EXPIRE,
|
||||||
settings.DOMAINS_DEFAULT_MIN_TTL if self.min_ttl is None else self.min_ttl,
|
self.min_ttl or settings.DOMAINS_DEFAULT_MIN_TTL,
|
||||||
]
|
]
|
||||||
records.insert(0, AttrDict(
|
records.insert(0, AttrDict(
|
||||||
type=Record.SOA,
|
type=Record.SOA,
|
||||||
|
@ -272,13 +272,24 @@ class Record(models.Model):
|
||||||
(SOA, "SOA"),
|
(SOA, "SOA"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
VALIDATORS = {
|
||||||
|
MX: validators.validate_mx_record,
|
||||||
|
NS: validators.validate_zone_label,
|
||||||
|
A: validate_ipv4_address,
|
||||||
|
AAAA: validate_ipv6_address,
|
||||||
|
CNAME: validators.validate_zone_label,
|
||||||
|
TXT: validate_ascii,
|
||||||
|
SRV: validators.validate_srv_record,
|
||||||
|
SOA: validators.validate_soa_record,
|
||||||
|
}
|
||||||
|
|
||||||
domain = models.ForeignKey(Domain, verbose_name=_("domain"), related_name='records')
|
domain = models.ForeignKey(Domain, verbose_name=_("domain"), related_name='records')
|
||||||
ttl = models.CharField(_("TTL"), max_length=8, blank=True,
|
ttl = models.CharField(_("TTL"), max_length=8, blank=True,
|
||||||
help_text=_("Record TTL, defaults to %s") % settings.DOMAINS_DEFAULT_TTL,
|
help_text=_("Record TTL, defaults to %s") % settings.DOMAINS_DEFAULT_TTL,
|
||||||
validators=[validators.validate_zone_interval])
|
validators=[validators.validate_zone_interval])
|
||||||
type = models.CharField(_("type"), max_length=32, choices=TYPE_CHOICES)
|
type = models.CharField(_("type"), max_length=32, choices=TYPE_CHOICES)
|
||||||
value = models.CharField(_("value"), max_length=256, help_text=_("MX, NS and CNAME records "
|
value = models.CharField(_("value"), max_length=256,
|
||||||
"sould end with a dot."))
|
help_text=_("MX, NS and CNAME records sould end with a dot."))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s %s IN %s %s" % (self.domain, self.get_ttl(), self.type, self.value)
|
return "%s %s IN %s %s" % (self.domain, self.get_ttl(), self.type, self.value)
|
||||||
|
@ -288,20 +299,13 @@ class Record(models.Model):
|
||||||
# validate value
|
# validate value
|
||||||
if self.type != self.TXT:
|
if self.type != self.TXT:
|
||||||
self.value = self.value.lower().strip()
|
self.value = self.value.lower().strip()
|
||||||
choices = {
|
if self.type:
|
||||||
self.MX: validators.validate_mx_record,
|
try:
|
||||||
self.NS: validators.validate_zone_label,
|
self.VALIDATORS[self.type](self.value)
|
||||||
self.A: validate_ipv4_address,
|
except ValidationError as error:
|
||||||
self.AAAA: validate_ipv6_address,
|
raise ValidationError({
|
||||||
self.CNAME: validators.validate_zone_label,
|
'value': error,
|
||||||
self.TXT: validate_ascii,
|
})
|
||||||
self.SRV: validators.validate_srv_record,
|
|
||||||
self.SOA: validators.validate_soa_record,
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
choices[self.type](self.value)
|
|
||||||
except ValidationError as error:
|
|
||||||
raise ValidationError({'value': error})
|
|
||||||
|
|
||||||
def get_ttl(self):
|
def get_ttl(self):
|
||||||
return self.ttl or settings.DOMAINS_DEFAULT_TTL
|
return self.ttl or settings.DOMAINS_DEFAULT_TTL
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends "admin/orchestra/generic_confirmation.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/admin/RelatedObjectLookups.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/jquery.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/actions.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/collapse.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'admin/js/inlines.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block formset %}
|
||||||
|
{% for formset in formsets %}
|
||||||
|
{{ formset.as_admin }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
|
@ -1,5 +1,6 @@
|
||||||
from threading import local
|
from threading import local
|
||||||
|
|
||||||
|
from django.contrib.admin.models import LogEntry
|
||||||
from django.core.urlresolvers import resolve
|
from django.core.urlresolvers import resolve
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
||||||
|
@ -15,14 +16,14 @@ from .models import BackendLog
|
||||||
|
|
||||||
@receiver(post_save, dispatch_uid='orchestration.post_save_collector')
|
@receiver(post_save, dispatch_uid='orchestration.post_save_collector')
|
||||||
def post_save_collector(sender, *args, **kwargs):
|
def post_save_collector(sender, *args, **kwargs):
|
||||||
if sender not in [BackendLog, Operation]:
|
if sender not in (BackendLog, Operation, LogEntry):
|
||||||
instance = kwargs.get('instance')
|
instance = kwargs.get('instance')
|
||||||
OperationsMiddleware.collect(Operation.SAVE, **kwargs)
|
OperationsMiddleware.collect(Operation.SAVE, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, dispatch_uid='orchestration.pre_delete_collector')
|
@receiver(pre_delete, dispatch_uid='orchestration.pre_delete_collector')
|
||||||
def pre_delete_collector(sender, *args, **kwargs):
|
def pre_delete_collector(sender, *args, **kwargs):
|
||||||
if sender not in [BackendLog, Operation]:
|
if sender not in (BackendLog, Operation, LogEntry):
|
||||||
OperationsMiddleware.collect(Operation.DELETE, **kwargs)
|
OperationsMiddleware.collect(Operation.DELETE, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,5 +62,5 @@ def history(modeladmin, request, queryset):
|
||||||
context = {
|
context = {
|
||||||
'resources': resources,
|
'resources': resources,
|
||||||
}
|
}
|
||||||
return render(request, 'admin/resources/resourcedata/report.html', context)
|
return render(request, 'admin/resources/resourcedata/history.html', context)
|
||||||
history.url_name = 'history'
|
history.url_name = 'history'
|
||||||
|
|
|
@ -254,16 +254,25 @@ def resource_inline_factory(resources):
|
||||||
display_updated = admin_date('updated_at', default=_("Never"))
|
display_updated = admin_date('updated_at', default=_("Never"))
|
||||||
|
|
||||||
def display_used(self, data):
|
def display_used(self, data):
|
||||||
|
from django.templatetags.static import static
|
||||||
update_link = ''
|
update_link = ''
|
||||||
history_link = ''
|
history_link = ''
|
||||||
if data.pk:
|
if data.pk:
|
||||||
update_url = reverse('admin:resources_resourcedata_monitor', args=(data.pk,))
|
context = {
|
||||||
update_link = '<a href="%s"><strong>%s</strong></a>' % (update_url, _("Update"))
|
'title': _("Update"),
|
||||||
history_url = reverse('admin:resources_resourcedata_history', args=(data.pk,))
|
'url': reverse('admin:resources_resourcedata_monitor', args=(data.pk,)),
|
||||||
popup = 'onclick="return showAddAnotherPopup(this);"'
|
'image': '<img src="%s"></img>' % static('orchestra/images/reload.png'),
|
||||||
history_link = '<a href="%s" %s>%s</a>' % (history_url, popup, _("History"))
|
}
|
||||||
|
update = '<a href="%(url)s" title="%(title)s">%(image)s</a>' % context
|
||||||
|
context.update({
|
||||||
|
'title': _("Show history"),
|
||||||
|
'image': '<img src="%s"></img>' % static('orchestra/images/history.png'),
|
||||||
|
'url': reverse('admin:resources_resourcedata_history', args=(data.pk,)),
|
||||||
|
'popup': 'onclick="return showAddAnotherPopup(this);"',
|
||||||
|
})
|
||||||
|
history = '<a href="%(url)s" title="%(title)s" %(popup)s>%(image)s</a>' % context
|
||||||
if data.used is not None:
|
if data.used is not None:
|
||||||
return ' '.join(map(str, (data.used, data.resource.unit, update_link, history_link)))
|
return ' '.join(map(str, (data.used, data.resource.unit, update, history)))
|
||||||
return _("Unknonw %s") % update_link
|
return _("Unknonw %s") % update_link
|
||||||
display_used.short_description = _("Used")
|
display_used.short_description = _("Used")
|
||||||
display_used.allow_tags = True
|
display_used.allow_tags = True
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Transaction Report</title>
|
<title>Resource history</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
<script src="/static/orchestra/js/Chart.min.js"></script>
|
<script src="/static/orchestra/js/Chart.min.js"></script>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
}
|
}
|
||||||
new Chart(income).Bar(barData, options);
|
new Chart(income).Bar(barData, options);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<table id="summary">
|
<table id="summary">
|
||||||
<tr class="header">
|
<tr class="header">
|
||||||
<th class="title column-name">{% trans "Date" %}</th>
|
<th class="title column-name">{% trans "Date" %}</th>
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,358 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
inkscape:export-ydpi="90.000000"
|
||||||
|
inkscape:export-xdpi="90.000000"
|
||||||
|
inkscape:export-filename="/home/glic3/orchestra/django-orchestra/orchestra/static/orchestra/images/history.png"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
id="svg11300"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.91 r"
|
||||||
|
sodipodi:docname="history.svg"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||||
|
version="1.1">
|
||||||
|
<defs
|
||||||
|
id="defs3">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient5204">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#c4a000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop5206" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#c4a000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop5208" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient5196">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#c4a000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop5198" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#c4a000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop5200" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient12512">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1.0000000;"
|
||||||
|
offset="0.0000000"
|
||||||
|
id="stop12513" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#fff520;stop-opacity:0.89108908;"
|
||||||
|
offset="0.50000000"
|
||||||
|
id="stop12517" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#fff300;stop-opacity:0.0000000;"
|
||||||
|
offset="1.0000000"
|
||||||
|
id="stop12514" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient12512"
|
||||||
|
id="radialGradient278"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="55"
|
||||||
|
cy="125"
|
||||||
|
fx="55"
|
||||||
|
fy="125"
|
||||||
|
r="14.375" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient10653">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#f3f4ff;stop-opacity:1.0000000;"
|
||||||
|
offset="0.0000000"
|
||||||
|
id="stop10655" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#9193af;stop-opacity:1.0000000;"
|
||||||
|
offset="1.0000000"
|
||||||
|
id="stop10657" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient42174">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#a0a0a0;stop-opacity:1.0000000;"
|
||||||
|
offset="0.0000000"
|
||||||
|
id="stop42176" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1.0000000;"
|
||||||
|
offset="1.0000000"
|
||||||
|
id="stop42178" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient2145">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#fffffd;stop-opacity:1.0000000;"
|
||||||
|
offset="0.0000000"
|
||||||
|
id="stop2147" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#cbcbc9;stop-opacity:1.0000000;"
|
||||||
|
offset="1.0000000"
|
||||||
|
id="stop2149" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient37935">
|
||||||
|
<stop
|
||||||
|
id="stop37937"
|
||||||
|
offset="0.0000000"
|
||||||
|
style="stop-color:#9497b3;stop-opacity:1.0000000;" />
|
||||||
|
<stop
|
||||||
|
id="stop37939"
|
||||||
|
offset="1.0000000"
|
||||||
|
style="stop-color:#4c4059;stop-opacity:1.0000000;" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient2152">
|
||||||
|
<stop
|
||||||
|
id="stop2154"
|
||||||
|
offset="0.0000000"
|
||||||
|
style="stop-color:#9aa29a;stop-opacity:1.0000000;" />
|
||||||
|
<stop
|
||||||
|
id="stop2156"
|
||||||
|
offset="1.0000000"
|
||||||
|
style="stop-color:#b5beb5;stop-opacity:1.0000000;" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2152"
|
||||||
|
id="linearGradient4307"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.1323593,0,0,0.35150265,-12.192171,24.700536)"
|
||||||
|
x1="8.9156475"
|
||||||
|
y1="37.197018"
|
||||||
|
x2="9.8855038"
|
||||||
|
y2="52.090679" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient10653"
|
||||||
|
id="radialGradient4309"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="11.3292"
|
||||||
|
cy="10.58397"
|
||||||
|
fx="11.3292"
|
||||||
|
fy="10.58397"
|
||||||
|
r="15.532059"
|
||||||
|
gradientTransform="matrix(0.49213503,0,0,0.49213503,0.00830489,31.624507)" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2145"
|
||||||
|
id="radialGradient4311"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="11.901996"
|
||||||
|
cy="10.045444"
|
||||||
|
fx="11.901996"
|
||||||
|
fy="10.045444"
|
||||||
|
r="29.292715"
|
||||||
|
gradientTransform="matrix(0.42187886,0,0,0.42187886,1.1156771,32.810317)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient42174"
|
||||||
|
id="linearGradient4313"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="6.342216"
|
||||||
|
y1="7.7893324"
|
||||||
|
x2="22.218424"
|
||||||
|
y2="25.884274"
|
||||||
|
gradientTransform="matrix(0.42187886,0,0,0.42187886,1.1156771,32.810317)" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient5196"
|
||||||
|
id="radialGradient5202"
|
||||||
|
cx="23.375"
|
||||||
|
cy="10.972863"
|
||||||
|
fx="23.375"
|
||||||
|
fy="10.972863"
|
||||||
|
r="3.3478093"
|
||||||
|
gradientTransform="matrix(2.3292353,0,0,2.4008659,-46.253114,13.661846)"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient5204"
|
||||||
|
id="linearGradient5210"
|
||||||
|
x1="19.667364"
|
||||||
|
y1="4.2570662"
|
||||||
|
x2="20.329933"
|
||||||
|
y2="5.2845874"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.64158826,0,0,0.64158826,-6.8043677,32.387358)" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient37935"
|
||||||
|
id="radialGradient5212"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="8.7468252"
|
||||||
|
cy="6.8283234"
|
||||||
|
fx="8.7468252"
|
||||||
|
fy="6.8283234"
|
||||||
|
r="29.889715"
|
||||||
|
gradientTransform="matrix(0.51891397,0,0,0.51891397,-0.4268392,31.2037)" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
stroke="#c4a000"
|
||||||
|
fill="#babdb6"
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="0.25490196"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.627416"
|
||||||
|
inkscape:cx="2.7636966"
|
||||||
|
inkscape:cy="11.830339"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:grid-bbox="true"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1024"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata4">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Jakub Steiner</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:source>http://jimmac.musichall.cz</dc:source>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
|
||||||
|
<dc:title>New Appointment</dc:title>
|
||||||
|
<dc:subject>
|
||||||
|
<rdf:Bag>
|
||||||
|
<rdf:li>appointment</rdf:li>
|
||||||
|
<rdf:li>new</rdf:li>
|
||||||
|
<rdf:li>meeting</rdf:li>
|
||||||
|
<rdf:li>rvsp</rdf:li>
|
||||||
|
</rdf:Bag>
|
||||||
|
</dc:subject>
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://web.resource.org/cc/Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
transform="translate(0,-32)">
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccc"
|
||||||
|
id="path14341"
|
||||||
|
d="m 6.1045434,32.312322 -5.20565338,6.051009 0.45627258,0.45064 4.7493808,-6.501649 z"
|
||||||
|
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient4307);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccc"
|
||||||
|
id="path18921"
|
||||||
|
d="M 6.0608909,32.279726 1.4186351,38.717102 2.081818,39.302631 6.0608909,32.279726 Z"
|
||||||
|
style="fill:#fefefe;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<circle
|
||||||
|
id="path27786"
|
||||||
|
style="fill:url(#radialGradient5212);fill-opacity:1;fill-rule:evenodd;stroke:#605773;stroke-width:0.36248955;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="8.0055094"
|
||||||
|
cy="39.978912"
|
||||||
|
r="7.7373796" />
|
||||||
|
<circle
|
||||||
|
id="path35549"
|
||||||
|
style="fill:url(#radialGradient4311);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4313);stroke-width:0.30012295;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="7.9712114"
|
||||||
|
cy="39.944607"
|
||||||
|
r="6.2905145" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient5202);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient5210);stroke-width:0.36248916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none"
|
||||||
|
id="path4120"
|
||||||
|
sodipodi:cx="8.1927567"
|
||||||
|
sodipodi:cy="40.006229"
|
||||||
|
sodipodi:rx="5.4535012"
|
||||||
|
sodipodi:ry="5.4535012"
|
||||||
|
d="m 3.8969257,36.646689 a 5.4535012,5.4535012 0 0 1 4.2686858,-2.093893 l 0.027145,5.453433 z"
|
||||||
|
sodipodi:start="3.8052902"
|
||||||
|
sodipodi:end="4.7074114" />
|
||||||
|
<circle
|
||||||
|
id="path34778"
|
||||||
|
style="fill:#f3f3f3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.36248925;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="8.1309881"
|
||||||
|
cy="40.029209"
|
||||||
|
r="0.91594833" />
|
||||||
|
<path
|
||||||
|
id="path35559"
|
||||||
|
d="M 7.4055247,39.313544 4.135537,36.5667"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.36248925;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path35561"
|
||||||
|
d="M 6.4021514,42.59715 7.4751372,40.968385"
|
||||||
|
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.72497851;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<circle
|
||||||
|
id="path35563"
|
||||||
|
style="opacity:1;fill:#b6b9b1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.36871839;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="7.9325924"
|
||||||
|
cy="34.972736"
|
||||||
|
r="0.61665297" />
|
||||||
|
<circle
|
||||||
|
id="path35565"
|
||||||
|
style="opacity:1;fill:#b6b9b1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.36871839;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="7.9325924"
|
||||||
|
cy="44.839195"
|
||||||
|
r="0.61665297" />
|
||||||
|
<circle
|
||||||
|
id="path35567"
|
||||||
|
style="opacity:1;fill:#b6b9b1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.36871839;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="2.9993658"
|
||||||
|
cy="39.905964"
|
||||||
|
r="0.61665297" />
|
||||||
|
<circle
|
||||||
|
id="path35569"
|
||||||
|
style="opacity:1;fill:#b6b9b1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.36871839;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
cx="12.865818"
|
||||||
|
cy="39.905964"
|
||||||
|
r="0.61665297" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient4309);stroke-width:0.36248961;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="path10651"
|
||||||
|
cx="8.0055075"
|
||||||
|
cy="39.946884"
|
||||||
|
r="7.3380861" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 828 B |
|
@ -0,0 +1,207 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
id="svg2473"
|
||||||
|
inkscape:version="0.91 r"
|
||||||
|
sodipodi:docname="reload.svg"
|
||||||
|
inkscape:export-filename="/home/glic3/orchestra/django-orchestra/orchestra/static/orchestra/images/reload.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<metadata
|
||||||
|
id="metadata37">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1024"
|
||||||
|
id="namedview35"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="14.75"
|
||||||
|
inkscape:cx="-3.3220339"
|
||||||
|
inkscape:cy="8"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2473" />
|
||||||
|
<defs
|
||||||
|
id="defs2475">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3719">
|
||||||
|
<stop
|
||||||
|
id="stop3721"
|
||||||
|
style="stop-color:#538ec6;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3723"
|
||||||
|
style="stop-color:#538ec6;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3709">
|
||||||
|
<stop
|
||||||
|
id="stop3711"
|
||||||
|
style="stop-color:#6396cd;stop-opacity:0"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3713"
|
||||||
|
style="stop-color:#6396cd;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3533">
|
||||||
|
<stop
|
||||||
|
id="stop3535"
|
||||||
|
style="stop-color:#93b9dd;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3545"
|
||||||
|
style="stop-color:#6396cd;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
x1="108.97799"
|
||||||
|
y1="230.02158"
|
||||||
|
x2="107.36588"
|
||||||
|
y2="224.30264"
|
||||||
|
id="linearGradient6283"
|
||||||
|
xlink:href="#linearGradient6277"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient6277">
|
||||||
|
<stop
|
||||||
|
id="stop6279"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop6281"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
x1="107.25907"
|
||||||
|
y1="222.87531"
|
||||||
|
x2="108.83574"
|
||||||
|
y2="226.83432"
|
||||||
|
id="linearGradient6291"
|
||||||
|
xlink:href="#linearGradient6285"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient6285">
|
||||||
|
<stop
|
||||||
|
id="stop6287"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop6289"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
x1="8"
|
||||||
|
y1="0"
|
||||||
|
x2="9"
|
||||||
|
y2="9"
|
||||||
|
id="linearGradient3697"
|
||||||
|
xlink:href="#linearGradient3533"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
x1="107.98247"
|
||||||
|
y1="219.63542"
|
||||||
|
x2="108.84705"
|
||||||
|
y2="227.40942"
|
||||||
|
id="linearGradient3705"
|
||||||
|
xlink:href="#linearGradient3533"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
x1="112.3054"
|
||||||
|
y1="227.40942"
|
||||||
|
x2="108.84705"
|
||||||
|
y2="230.86455"
|
||||||
|
id="linearGradient3715"
|
||||||
|
xlink:href="#linearGradient3709"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
x1="110.58552"
|
||||||
|
y1="230.02382"
|
||||||
|
x2="114.06341"
|
||||||
|
y2="227.41991"
|
||||||
|
id="linearGradient3725"
|
||||||
|
xlink:href="#linearGradient3719"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
transform="translate(-103,-219)"
|
||||||
|
id="g6293"
|
||||||
|
style="stroke:#000000;stroke-opacity:1;display:inline;enable-background:new">
|
||||||
|
<path
|
||||||
|
d="m 112.89199,228.29933 a 4.75,4.75 0 0 1 -6.41224,0.8092"
|
||||||
|
transform="matrix(1.1501219,0,0,1.1521155,-15.186827,-33.014002)"
|
||||||
|
id="path4517-5"
|
||||||
|
style="color:#000000;fill:none;stroke:url(#linearGradient3725);stroke-width:3.47488189;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
<path
|
||||||
|
d="m 116,221 0,4 -4,0"
|
||||||
|
id="path4519-1-9"
|
||||||
|
style="fill:none;stroke:#538ec6;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<path
|
||||||
|
d="m 106.86806,229.35961 a 4.75,4.75 0 1 1 6.22978,-6.89468"
|
||||||
|
transform="matrix(1.1501219,0,0,1.1521155,-15.186827,-33.014002)"
|
||||||
|
id="path3717"
|
||||||
|
style="color:#000000;fill:none;stroke:#538ec6;stroke-width:3.47488189;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 107.99194,229.83037 a 4.75,4.75 0 1 1 5.1059,-7.36544"
|
||||||
|
transform="matrix(1.1566222,0,0,1.1577034,-118.89492,-253.27267)"
|
||||||
|
id="path4517"
|
||||||
|
style="color:#000000;fill:none;stroke:url(#linearGradient3705);stroke-width:1.72836542;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
<path
|
||||||
|
d="m 12.999998,2 0,4 -4.0000006,0"
|
||||||
|
id="path4519-7"
|
||||||
|
style="fill:none;stroke:url(#linearGradient3697);stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;enable-background:new" />
|
||||||
|
<path
|
||||||
|
d="m 11,5.5 -2.0000026,0 M 12.499998,2 l 0,1.3"
|
||||||
|
id="path4519-8-7"
|
||||||
|
style="opacity:0.23999999;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;enable-background:new" />
|
||||||
|
<path
|
||||||
|
d="m 112.88153,228.31178 a 4.75,4.75 0 0 1 -5.33461,1.37241"
|
||||||
|
transform="matrix(1.1566222,0,0,1.1577034,-118.89492,-253.27267)"
|
||||||
|
id="path3707"
|
||||||
|
style="color:#000000;fill:none;stroke:url(#linearGradient3715);stroke-width:1.72836542;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
<path
|
||||||
|
d="m 106.51558,229.134 a 4.75,4.75 0 1 1 6.58226,-6.66907"
|
||||||
|
transform="matrix(1.2684974,0,0,1.2629385,-131.05785,-276.9778)"
|
||||||
|
id="path4517-8"
|
||||||
|
style="opacity:0.23999999;color:#000000;fill:none;stroke:url(#linearGradient6291);stroke-width:0.79006732;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
<path
|
||||||
|
d="m 111.90936,229.18578 a 4.75,4.75 0 1 1 1.18848,-6.72085"
|
||||||
|
transform="matrix(1.0467613,0,0,1.0546091,-106.88644,-230.0516)"
|
||||||
|
id="path4517-8-9"
|
||||||
|
style="opacity:0.23999999;color:#000000;fill:none;stroke:url(#linearGradient6283);stroke-width:0.95176643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.1 KiB |
|
@ -32,6 +32,7 @@
|
||||||
<p>{{ content_message | safe }}</p>
|
<p>{{ content_message | safe }}</p>
|
||||||
<ul>{{ display_objects | unordered_list }}</ul>
|
<ul>{{ display_objects | unordered_list }}</ul>
|
||||||
<form action="" method="post">{% csrf_token %}
|
<form action="" method="post">{% csrf_token %}
|
||||||
|
{% block form %}
|
||||||
{% if form %}
|
{% if form %}
|
||||||
<fieldset class="module aligned">
|
<fieldset class="module aligned">
|
||||||
{{ form.non_field_errors }}
|
{{ form.non_field_errors }}
|
||||||
|
@ -50,9 +51,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block formset %}
|
||||||
{% if formset %}
|
{% if formset %}
|
||||||
{{ formset.as_admin }}
|
{{ formset.as_admin }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
<div>
|
<div>
|
||||||
{% for obj in queryset %}
|
{% for obj in queryset %}
|
||||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||||
|
|
|
@ -40,7 +40,7 @@ def _un(singular__plural, n=None):
|
||||||
return ungettext(singular, plural, n)
|
return ungettext(singular, plural, n)
|
||||||
|
|
||||||
|
|
||||||
def naturaldatetime(date, include_seconds=False):
|
def naturaldatetime(date, include_seconds=True):
|
||||||
"""Convert datetime into a human natural date string."""
|
"""Convert datetime into a human natural date string."""
|
||||||
if not date:
|
if not date:
|
||||||
return ''
|
return ''
|
||||||
|
@ -56,35 +56,35 @@ def naturaldatetime(date, include_seconds=False):
|
||||||
minutes = delta.seconds / 60
|
minutes = delta.seconds / 60
|
||||||
seconds = delta.seconds
|
seconds = delta.seconds
|
||||||
|
|
||||||
ago = ' ago'
|
ago = " ago"
|
||||||
if days < 0:
|
if days < 0:
|
||||||
ago = ''
|
ago = ""
|
||||||
days = abs(days)
|
days = abs(days)
|
||||||
|
|
||||||
if days == 0:
|
if days == 0:
|
||||||
if hours == 0:
|
if hours == 0:
|
||||||
if minutes > 0:
|
if minutes >= 1:
|
||||||
minutes = float(seconds)/60
|
minutes = float(seconds)/60
|
||||||
return ungettext(
|
return ungettext(
|
||||||
_('{minutes:.1f} minute{ago}'),
|
_("{minutes:.1f} minute{ago}"),
|
||||||
_('{minutes:.1f} minutes{ago}'), minutes
|
_("{minutes:.1f} minutes{ago}"), minutes
|
||||||
).format(minutes=minutes, ago=ago)
|
).format(minutes=minutes, ago=ago)
|
||||||
else:
|
else:
|
||||||
if include_seconds and seconds:
|
if include_seconds:
|
||||||
return ungettext(
|
return ungettext(
|
||||||
_('{seconds} second{ago}'),
|
_("{seconds} second{ago}"),
|
||||||
_('{seconds} seconds{ago}'), seconds
|
_("{seconds} seconds{ago}"), seconds
|
||||||
).format(seconds=seconds, ago=ago)
|
).format(seconds=seconds, ago=ago)
|
||||||
return _('just now')
|
return _("just now")
|
||||||
else:
|
else:
|
||||||
hours = float(minutes)/60
|
hours = float(minutes)/60
|
||||||
return ungettext(
|
return ungettext(
|
||||||
_('{hours:.1f} hour{ago}'),
|
_("{hours:.1f} hour{ago}"),
|
||||||
_('{hours:.1f} hours{ago}'), hours
|
_("{hours:.1f} hours{ago}"), hours
|
||||||
).format(hours=hours, ago=ago)
|
).format(hours=hours, ago=ago)
|
||||||
|
|
||||||
if delta_midnight.days == 0:
|
if delta_midnight.days == 0:
|
||||||
return _('yesterday at {time}').format(time=date.strftime('%H:%M'))
|
return _("yesterday at {time}").format(time=date.strftime('%H:%M'))
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for chunk, pluralizefun in OLDER_CHUNKS:
|
for chunk, pluralizefun in OLDER_CHUNKS:
|
||||||
|
|
Loading…
Reference in New Issue