Refactores plugins, fixes on mailing contacts and improved resource validation

This commit is contained in:
Marc Aymerich 2014-11-24 14:39:41 +00:00
parent e962e5d7b2
commit 4732925f63
31 changed files with 188 additions and 105 deletions

10
TODO.md
View File

@ -149,20 +149,16 @@
* Resource used_list_display=True, allocated_list_displat=True, allow resources to show up on list_display * Resource used_list_display=True, allocated_list_displat=True, allow resources to show up on list_display
* Move plugins back from apps to orchestra main app
* BackendLog.updated_at (tasks that run over several minutes when finished they do not appear first on the changelist) (like celery tasks.when) * BackendLog.updated_at (tasks that run over several minutes when finished they do not appear first on the changelist) (like celery tasks.when)
* Validate a model path exists between resource.content_type and backend.model
* Periodic task for cleaning old monitoring data * Periodic task for cleaning old monitoring data
* Generate reports of Account contracted services
* Create an admin service_view with icons (like SaaS app) * Create an admin service_view with icons (like SaaS app)
* Fix ftp traffic * Fix ftp traffic
* Resource graph for each related object * Resource graph for each related object
* contacts filter by email_usage fix exact for contains * Rename apache logs ending on .log in order to logrotate easily
* SaaS wordpress multiple blogs per user? separate users from sites?

View File

@ -3,8 +3,9 @@ from django.core.mail import send_mass_mail
from django.shortcuts import render from django.shortcuts import render
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ungettext, ugettext_lazy as _
from orchestra.admin.utils import change_url from .. import settings
from .utils import change_url
from .forms import SendEmailForm from .forms import SendEmailForm
@ -13,18 +14,19 @@ class SendEmail(object):
short_description = _("Send email") short_description = _("Send email")
form = SendEmailForm form = SendEmailForm
template = 'admin/orchestra/generic_confirmation.html' template = 'admin/orchestra/generic_confirmation.html'
default_from = settings.ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL
__name__ = 'semd_email' __name__ = 'semd_email'
def __call__(self, modeladmin, request, queryset): def __call__(self, modeladmin, request, queryset):
""" make this monster behave like a function """ """ make this monster behave like a function """
self.modeladmin = modeladmin self.modeladmin = modeladmin
self.queryset = queryset self.queryset = queryset
opts = modeladmin.model._meta self.opts = modeladmin.model._meta
app_label = opts.app_label app_label = self.opts.app_label
self.context = { self.context = {
'action_name': _("Send email"), 'action_name': _("Send email"),
'action_value': self.__name__, 'action_value': self.__name__,
'opts': opts, 'opts': self.opts,
'app_label': app_label, 'app_label': app_label,
'queryset': queryset, 'queryset': queryset,
'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME, 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
@ -34,14 +36,17 @@ class SendEmail(object):
def write_email(self, request): def write_email(self, request):
if not request.user.is_superuser: if not request.user.is_superuser:
raise PermissionDenied raise PermissionDenied
form = self.form() initial={
'email_from': self.default_from,
'to': ' '.join(self.queryset.values_list('email', flat=True))
}
form = self.form(initial=initial)
if request.POST.get('post'): if request.POST.get('post'):
form = self.form(request.POST) form = self.form(request.POST, initial=initial)
if form.is_valid(): if form.is_valid():
options = { options = {
'email_from': form.cleaned_data['email_from'], 'email_from': form.cleaned_data['email_from'],
'cc': form.cleaned_data['cc'], 'extra_to': form.cleaned_data['extra_to'],
'bcc': form.cleaned_data['bcc'],
'subject': form.cleaned_data['subject'], 'subject': form.cleaned_data['subject'],
'message': form.cleaned_data['message'], 'message': form.cleaned_data['message'],
@ -50,7 +55,7 @@ class SendEmail(object):
opts = self.modeladmin.model._meta opts = self.modeladmin.model._meta
app_label = opts.app_label app_label = opts.app_label
self.context.update({ self.context.update({
'title': _("Send e-mail to contacts"), 'title': _("Send e-mail to %s") % self.opts.verbose_name_plural,
'content_title': "", 'content_title': "",
'form': form, 'form': form,
'submit_value': _("Continue"), 'submit_value': _("Continue"),
@ -61,31 +66,36 @@ class SendEmail(object):
def confirm_email(self, request, **options): def confirm_email(self, request, **options):
num = len(self.queryset) num = len(self.queryset)
email_from = options['email_from'] email_from = options['email_from']
bcc = options['bcc'] extra_to = options['extra_to']
to = options['cc']
subject = options['subject'] subject = options['subject']
message = options['message'] message = options['message']
# The user has already confirmed # The user has already confirmed
if request.POST.get('post') == 'email_confirmation': if request.POST.get('post') == 'email_confirmation':
emails = []
for contact in self.queryset.all(): for contact in self.queryset.all():
to.append(contact.email) emails.append((subject, message, email_from, [contact.email]))
send_mass_mail(subject, message, email_from, to, bcc) if extra_to:
emails.append((subject, message, email_from, extra_to))
send_mass_mail(emails)
msg = ungettext( msg = ungettext(
_("Message has been sent to %s.") % str(contact), _("Message has been sent to %s.") % str(contact),
_("Message has been sent to %i contacts.") % num, _("Message has been sent to %i %s.") % (num, self.opts.verbose_name_plural),
num num
) )
self.modeladmin.message_user(request, msg) self.modeladmin.message_user(request, msg)
return None return None
form = self.form(initial={ form = self.form(initial={
'email_from': email_from,
'extra_to': ', '.join(extra_to),
'subject': subject, 'subject': subject,
'message': message 'message': message
}) })
self.context.update({ self.context.update({
'title': _("Are you sure?"), 'title': _("Are you sure?"),
'content_message': _( 'content_message': _(
"Are you sure you want to send the following message to the following contacts?"), "Are you sure you want to send the following message to the following %s?"
) % self.opts.verbose_name_plural,
'display_objects': ["%s (%s)" % (contact, contact.email) for contact in self.queryset], 'display_objects': ["%s (%s)" % (contact, contact.email) for contact in self.queryset],
'form': form, 'form': form,
'subject': subject, 'subject': subject,

View File

@ -2,10 +2,13 @@ from functools import partial
from django import forms from django import forms
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.core import validators
from django.forms.models import modelformset_factory, BaseModelFormSet from django.forms.models import modelformset_factory, BaseModelFormSet
from django.template import Template, Context from django.template import Template, Context
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.forms.widgets import ShowTextWidget, ReadOnlyWidget
from ..core.validators import validate_password from ..core.validators import validate_password
@ -129,3 +132,39 @@ class AdminPasswordChangeForm(forms.Form):
return ['password'] return ['password']
changed_data = property(_get_changed_data) changed_data = property(_get_changed_data)
class SendEmailForm(forms.Form):
email_from = forms.EmailField(label=_("From"),
widget=forms.TextInput(attrs={'size':'118'}))
to = forms.CharField(label="To", required=False,
widget=ShowTextWidget())
extra_to = forms.CharField(label="To (extra)", required=False,
widget=forms.TextInput(attrs={'size':'118'}))
subject = forms.CharField(label=_("Subject"),
widget=forms.TextInput(attrs={'size':'118'}))
message = forms.CharField(label=_("Message"),
widget=forms.Textarea(attrs={'cols': 118, 'rows': 15}))
def __init__(self, *args, **kwargs):
super(SendEmailForm, self).__init__(*args, **kwargs)
initial = kwargs.get('initial')
if 'to' in initial:
self.fields['to'].widget = ReadOnlyWidget(initial['to'])
else:
self.fields.pop('to')
def clean_comma_separated_emails(self, value):
clean_value = []
for email in value.split(','):
email = email.strip()
if email:
try:
validators.validate_email(email)
except validators.ValidationError:
raise validators.ValidationError("Comma separated email addresses.")
clean_value.append(email)
return clean_value
def clean_extra_to(self):
extra_to = self.cleaned_data['extra_to']
return self.clean_comma_separated_emails(extra_to)

View File

@ -39,13 +39,22 @@ list_contacts.verbose_name = _("List contacts")
def service_report(modeladmin, request, queryset): def service_report(modeladmin, request, queryset):
accounts = [] accounts = []
for account in queryset: fields = []
# First we get related manager names to fire a prefetch related
for name, field in queryset.model._meta._name_map.iteritems():
model = field[0].model
if model in services.get() and model != queryset.model:
fields.append((model, name))
sorted(fields, key=lambda i: i[0]._meta.verbose_name_plural.lower())
fields = [field for model, field in fields]
for account in queryset.prefetch_related(*fields):
items = [] items = []
for service in services.get(): for field in fields:
if service != type(account): related_manager = getattr(account, field)
items.append((service._meta, service.objects.filter(account=account))) items.append((related_manager.model._meta, related_manager.all()))
sorted(items, key=lambda i: i[0].verbose_name_plural.lower())
accounts.append((account, items)) accounts.append((account, items))
context = { context = {
'accounts': accounts, 'accounts': accounts,
'date': timezone.now().today() 'date': timezone.now().today()

View File

@ -13,6 +13,7 @@ from django.utils.six.moves.urllib.parse import parse_qsl
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import SendEmail
from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url
from orchestra.core import services, accounts from orchestra.core import services, accounts
from orchestra.forms import UserChangeForm from orchestra.forms import UserChangeForm
@ -61,7 +62,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
filter_horizontal = () filter_horizontal = ()
change_readonly_fields = ('username', 'main_systemuser_link') change_readonly_fields = ('username', 'main_systemuser_link')
change_form_template = 'admin/accounts/account/change_form.html' change_form_template = 'admin/accounts/account/change_form.html'
actions = [disable, list_contacts, service_report] actions = [disable, list_contacts, service_report, SendEmail()]
change_view_actions = [disable, service_report] change_view_actions = [disable, service_report]
list_select_related = ('billcontact',) list_select_related = ('billcontact',)
ordering = () ordering = ()

View File

@ -1,7 +1,7 @@
{% load utils %} {% load utils i18n %}
<html> <html>
<head> <head>
<title>{% block title %}Service Report{% endblock %}</title> <title>{% block title %}Account service report{% endblock %}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
{% block head %}{% endblock %} {% block head %}{% endblock %}
<style type="text/css"> <style type="text/css">
@ -11,11 +11,11 @@
float: none !important; float: none !important;
font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif;
font-size: 12px; font-size: 12px;
color: #2E2E2E; color: #444;
} }
#date { #date {
float: right; float: right;
color: #666; color: rgb(102, 102, 102);
} }
.account-content { .account-content {
margin: 0px 0px 40px 20px; margin: 0px 0px 40px 20px;
@ -23,27 +23,38 @@
.item-title { .item-title {
list-style-type: none; list-style-type: none;
font-weight: bold; font-weight: bold;
color: #666;
} }
.items-ul { .items-ul {
padding: 0px; padding: 0px;
margin: 5px 0px 10px 20px; margin: 5px 0px 10px 20px;
} }
.related {
list-style: disc;
}
hr {
margin-top: -9px;
}
a {
text-decoration: none;
color: rgb(91, 128, 178);
}
</style> </style>
</head> </head>
<body> <body>
<div id="date">Service report generated on {{ date | date }}</div> <div id="date">{% trans "Service report generated on" %} {{ date | date }}</div>
{% for account, items in accounts %} {% for account, items in accounts %}
<h3>{{ account.get_full_name }} ({{ account.username }})</h3> <h3>{{ account.get_full_name }} - <a href="{{ account|admin_url }}">{{ account.username }}</a></h3>
<hr> <hr>
<div class="account-content"> <div class="account-content">
{{ account.get_type_display }} account registered on {{ account.date_joined | date }}<br> {{ account.get_type_display }} {% trans "account registered on" %} {{ account.date_joined | date }}<br>
<ul class="items-ul"> <ul class="items-ul">
{% for opts, related in items %} {% for opts, related in items %}
<li class="item-title">{{ opts.verbose_name_plural|capfirst }}</li> <li class="item-title">{{ opts.verbose_name_plural|capfirst }}</li>
<ul> <ul>
{% for obj in related %} {% for obj in related %}
<li>{{ obj }}{% if not obj|isactive %} (disabled){% endif %}</li> <li class="related"><a href="{{ obj|admin_url }}">{{ obj }}</a>{% if not obj|isactive %} ({% trans "disabled" %}){% endif %}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endfor %} {% endfor %}

View File

@ -3,11 +3,12 @@ from django.contrib import admin
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin
from orchestra.admin.actions import SendEmail
from orchestra.admin.utils import insertattr, admin_link, change_url from orchestra.admin.utils import insertattr, admin_link, change_url
from orchestra.apps.accounts.admin import AccountAdmin, AccountAdminMixin from orchestra.apps.accounts.admin import AccountAdmin, AccountAdminMixin
from orchestra.forms.widgets import paddingCheckboxSelectMultiple from orchestra.forms.widgets import paddingCheckboxSelectMultiple
from .actions import SendEmail from .filters import EmailUsageListFilter
from .models import Contact from .models import Contact
@ -16,7 +17,7 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link' 'dispaly_name', 'email', 'phone', 'phone2', 'country', 'account_link'
) )
# TODO email usage custom filter contains # TODO email usage custom filter contains
list_filter = ('email_usage',) list_filter = (EmailUsageListFilter,)
search_fields = ( search_fields = (
'account__username', 'account__full_name', 'short_name', 'full_name', 'phone', 'phone2', 'account__username', 'account__full_name', 'short_name', 'full_name', 'phone', 'phone2',
'email' 'email'

View File

@ -0,0 +1,16 @@
from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _
from .models import Contact
class EmailUsageListFilter(SimpleListFilter):
title = _("email usages")
parameter_name = 'email_usages'
def lookups(self, request, model_admin):
return Contact.email_usage.field.choices
def queryset(self, request, queryset):
value = self.value().split(',')
return queryset.filter(email_usages=value)

View File

@ -1,36 +0,0 @@
from django import forms
from django.core import validators
from django.utils.translation import ungettext, ugettext_lazy as _
from orchestra.forms.widgets import ShowTextWidget
from . import settings
class SendEmailForm(forms.Form):
email_from = forms.EmailField(label=_("From"),
initial=settings.CONTACTS_DEFAULT_FROM_EMAIL,
widget=forms.TextInput(attrs={'size':'118'}))
cc = forms.CharField(label="CC", required=False,
widget=forms.TextInput(attrs={'size':'118'}))
bcc = forms.CharField(label="BCC", required=False,
widget=forms.TextInput(attrs={'size':'118'}))
subject = forms.CharField(label=_("Subject"),
widget=forms.TextInput(attrs={'size':'118'}))
message = forms.CharField(label=_("Message"),
widget=forms.Textarea(attrs={'cols': 118, 'rows': 15}))
def clean_space_separated_emails(self, value):
value = value.split()
for email in value:
try:
validators.validate_email(email)
except validators.ValidationError:
raise validators.ValidationError("Space separated emails.")
return value
def clean_cc(self):
return self.clean_space_separated_emails(self.cleaned_data['cc'])
def clean_bcc(self):
return self.clean_space_separated_emails(self.cleaned_data['bcc'])

View File

@ -2,18 +2,22 @@ from django.conf import settings
from django_countries import data from django_countries import data
CONTACTS_DEFAULT_EMAIL_USAGES = getattr(settings, 'CONTACTS_DEFAULT_EMAIL_USAGES', CONTACTS_DEFAULT_EMAIL_USAGES = getattr(settings, 'CONTACTS_DEFAULT_EMAIL_USAGES', (
('SUPPORT', 'ADMIN', 'BILLING', 'TECH', 'ADDS', 'EMERGENCY') 'SUPPORT',
) 'ADMIN',
'BILLING',
'TECH',
'ADDS',
'EMERGENCY'
))
CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY', 'Barcelona') CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY', 'Barcelona')
CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', ((k,v) for k,v in data.COUNTRIES.iteritems())) CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', (
(k,v) for k,v in data.COUNTRIES.iteritems()
))
CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY', 'ES') CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY', 'ES')
CONTACTS_DEFAULT_FROM_EMAIL = getattr(settings, 'CONTACTS_DEFAULT_FROM_EMAIL', 'support@orchestra.lan')

View File

@ -1,11 +1,10 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class TopDomainListFilter(SimpleListFilter): class TopDomainListFilter(SimpleListFilter):
""" Filter Nodes by group according to request.user """ """ Filter Nodes by group according to request.user """
title = _("Top domains") title = _("top domains")
parameter_name = 'top_domain' parameter_name = 'top_domain'
def lookups(self, request, model_admin): def lookups(self, request, model_admin):

View File

@ -8,8 +8,8 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link from orchestra.admin.utils import admin_link
from orchestra.apps.accounts.admin import AccountAdminMixin from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.apps.plugins import PluginModelAdapter from orchestra.plugins import PluginModelAdapter
from orchestra.apps.plugins.admin import SelectPluginAdminMixin from orchestra.plugins.admin import SelectPluginAdminMixin
from . import settings from . import settings
from .models import MiscService, Miscellaneous from .models import MiscService, Miscellaneous
@ -26,7 +26,9 @@ class MiscServiceAdmin(ExtendedModelAdmin):
) )
list_editable = ('is_active',) list_editable = ('is_active',)
list_filter = ('has_identifier', 'has_amount', 'is_active') list_filter = ('has_identifier', 'has_amount', 'is_active')
fields = ('verbose_name', 'name', 'description', 'has_identifier', 'has_amount', 'is_active') fields = (
'verbose_name', 'name', 'description', 'has_identifier', 'has_amount', 'is_active'
)
prepopulated_fields = {'name': ('verbose_name',)} prepopulated_fields = {'name': ('verbose_name',)}
change_readonly_fields = ('name',) change_readonly_fields = ('name',)
@ -51,9 +53,12 @@ class MiscServiceAdmin(ExtendedModelAdmin):
class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelAdmin): class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelAdmin):
list_display = ('__unicode__', 'service_link', 'amount', 'dispaly_active', 'account_link') list_display = (
'__unicode__', 'service_link', 'amount', 'dispaly_active', 'account_link'
)
list_filter = ('service__name', 'is_active') list_filter = ('service__name', 'is_active')
list_select_related = ('service', 'account') list_select_related = ('service', 'account')
search_fields = ('identifier', 'description')
plugin_field = 'service' plugin_field = 'service'
plugin = MiscServicePlugin plugin = MiscServicePlugin

View File

@ -5,7 +5,7 @@ from django.utils import timezone
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.apps import plugins from orchestra import plugins
from . import methods from . import methods

View File

@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
from orchestra.admin.utils import admin_colored, admin_link from orchestra.admin.utils import admin_colored, admin_link
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.apps.plugins.admin import SelectPluginAdminMixin from orchestra.plugins.admin import SelectPluginAdminMixin
from . import actions from . import actions
from .methods import PaymentMethod from .methods import PaymentMethod

View File

@ -2,7 +2,7 @@ from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from orchestra.apps.plugins.forms import PluginDataForm from orchestra.plugins.forms import PluginDataForm
from .options import PaymentMethod from .options import PaymentMethod

View File

@ -2,7 +2,7 @@ from dateutil import relativedelta
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from orchestra.apps import plugins from orchestra import plugins
from orchestra.utils.functional import cached from orchestra.utils.functional import cached
from orchestra.utils.python import import_class from orchestra.utils.python import import_class

View File

@ -12,7 +12,7 @@ from django_iban.forms import IBANFormField
from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH
from rest_framework import serializers from rest_framework import serializers
from orchestra.apps.plugins.forms import PluginDataForm from orchestra.plugins.forms import PluginDataForm
from .. import settings from .. import settings
from .options import PaymentMethod from .options import PaymentMethod

View File

@ -86,6 +86,21 @@ class Resource(models.Model):
def clean(self): def clean(self):
self.verbose_name = self.verbose_name.strip() self.verbose_name = self.verbose_name.strip()
# Validate that model path exists between ct and each monitor.model
monitor_errors = []
for monitor in self.monitors:
try:
self.get_model_path(monitor)
except (RuntimeError, LookupError):
monitor_errors.append(monitor)
if monitor_errors:
raise validators.ValidationError({
'monitors': [
_("Path does not exists between '%s' and '%s'") % (
get_model(ServiceMonitor.get_backend(monitor).model)._meta.model_name,
self.content_type.model_class()._meta.model_name,
) for error in monitor_errors
]})
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
created = not self.pk created = not self.pk
@ -99,6 +114,13 @@ class Resource(models.Model):
super(Resource, self).delete(*args, **kwargs) super(Resource, self).delete(*args, **kwargs)
name = 'monitor.%s' % str(self) name = 'monitor.%s' % str(self)
def get_model_path(self, monitor):
""" returns a model path between self.content_type and monitor.model """
resource_model = self.content_type.model_class()
model_path = ServiceMonitor.get_backend(monitor).model
monitor_model = get_model(model_path)
return get_model_field_path(monitor_model, resource_model)
def sync_periodic_task(self): def sync_periodic_task(self):
name = 'monitor.%s' % str(self) name = 'monitor.%s' % str(self)
if self.pk and self.crontab: if self.pk and self.crontab:
@ -190,17 +212,14 @@ class ResourceData(models.Model):
today = timezone.now() today = timezone.now()
datasets = [] datasets = []
for monitor in resource.monitors: for monitor in resource.monitors:
resource_model = self.content_type.model_class() path = self.resource.get_model_path(monitor)
model_path = ServiceMonitor.get_backend(monitor).model if path == []:
monitor_model = get_model(model_path)
if resource_model == monitor_model:
dataset = MonitorData.objects.filter( dataset = MonitorData.objects.filter(
monitor=monitor, monitor=monitor,
content_type=self.content_type_id, content_type=self.content_type_id,
object_id=self.object_id object_id=self.object_id
) )
else: else:
path = get_model_field_path(monitor_model, resource_model)
fields = '__'.join(path) fields = '__'.join(path)
objects = monitor_model.objects.filter(**{fields: self.object_id}) objects = monitor_model.objects.filter(**{fields: self.object_id})
pks = objects.values_list('id', flat=True) pks = objects.values_list('id', flat=True)

View File

@ -1,8 +1,11 @@
from django.core.validators import ValidationError from django.core.validators import ValidationError
from django.utils.translation import ugettext_lazy as _
def validate_scale(value): def validate_scale(value):
try: try:
int(eval(value)) int(eval(value))
except ValueError: except Exception, e:
raise ValidationError(_("%s value is not a valid scale expression")) raise ValidationError(
_("'%s' is not a valid scale expression. (%s)") % (value, str(e))
)

View File

@ -2,7 +2,7 @@ from django.contrib import admin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.apps.accounts.admin import AccountAdminMixin from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.apps.plugins.admin import SelectPluginAdminMixin from orchestra.plugins.admin import SelectPluginAdminMixin
from .models import SaaS from .models import SaaS
from .services import SoftwareService from .services import SoftwareService

View File

@ -1,7 +1,7 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.apps.plugins.forms import PluginDataForm from orchestra.plugins.forms import PluginDataForm
from .options import SoftwareService from .options import SoftwareService

View File

@ -3,8 +3,8 @@ from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe 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.apps import plugins from orchestra import plugins
from orchestra.apps.plugins.forms import PluginDataForm from orchestra.plugins.forms import PluginDataForm
from orchestra.core import validators from orchestra.core import validators
from orchestra.forms import widgets from orchestra.forms import widgets
from orchestra.utils.functional import cached from orchestra.utils.functional import cached
@ -72,6 +72,7 @@ class SoftwareServiceForm(PluginDataForm):
obj.set_password(self.cleaned_data["password1"]) obj.set_password(self.cleaned_data["password1"])
return obj return obj
class SoftwareService(plugins.Plugin): class SoftwareService(plugins.Plugin):
form = SoftwareServiceForm form = SoftwareServiceForm
serializer = None serializer = None

View File

@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.apps import plugins from orchestra import plugins
from orchestra.utils.humanize import text2int from orchestra.utils.humanize import text2int
from orchestra.utils.python import AttrDict from orchestra.utils.python import AttrDict

View File

@ -86,7 +86,6 @@ INSTALLED_APPS = (
'orchestra.apps.miscellaneous', 'orchestra.apps.miscellaneous',
'orchestra.apps.bills', 'orchestra.apps.bills',
'orchestra.apps.payments', 'orchestra.apps.payments',
'orchestra.apps.plugins',
# Third-party apps # Third-party apps
'django_extensions', 'django_extensions',

View File

@ -47,3 +47,4 @@ def get_model_field_path(origin, target):
new_path = list(path) new_path = list(path)
new_path.append(field.name) new_path.append(field.name)
queue.append((new_model, new_path)) queue.append((new_model, new_path))
raise LookupError("Path does not exists between '%s' and '%s' models" % (origin, target))

View File

@ -31,3 +31,8 @@ API_ROOT_VIEW = getattr(settings, 'API_ROOT_VIEW', 'orchestra.api.root.APIRoot')
ORCHESTRA_MIGRATION_MODE = getattr(settings, 'ORCHESTRA_MIGRATION_MODE', False) ORCHESTRA_MIGRATION_MODE = getattr(settings, 'ORCHESTRA_MIGRATION_MODE', False)
ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = getattr(settings, 'ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL',
'support@orchestra.lan'
)