Improved variable names consistency, cleaned up *settings.py and refactored resource aggregation
This commit is contained in:
parent
bcfc453a95
commit
9e59346042
57
TODO.md
57
TODO.md
|
@ -20,7 +20,7 @@
|
|||
|
||||
* backend logs with hal logo
|
||||
|
||||
* LAST version of this shit http://wkhtmltopdf.org/downloads.html
|
||||
* LAST version of this shit http://wkhtmltopdf.org/downloads.h otml
|
||||
|
||||
* translations
|
||||
from django.utils import translation
|
||||
|
@ -137,17 +137,15 @@
|
|||
|
||||
* Resource graph for each related object
|
||||
|
||||
* Service.account change and orders consistency
|
||||
|
||||
* SaaS model splitted into SaaSUser and SaaSSite? inherit from SaaS
|
||||
|
||||
* prevent @pangea.org email addresses on contacts, enforce at least one email without @pangea.org
|
||||
|
||||
* forms autocomplete="off", doesn't work in chrome
|
||||
|
||||
|
||||
ln -s /proc/self/fd /dev/fd
|
||||
|
||||
* escape passwords and not allow ' on them !
|
||||
|
||||
|
||||
POST INSTALL
|
||||
|
@ -160,15 +158,12 @@ ssh-copy-id root@<server-address>
|
|||
Php binaries should have this format: /usr/bin/php5.2-cgi
|
||||
|
||||
|
||||
|
||||
* logs on panel/logs/ ? mkdir ~webapps, backend post save signal?
|
||||
* transaction fault tolerant on backend.execute()
|
||||
* <IfModule security2_module> and other IfModule on backend SecRule
|
||||
|
||||
|
||||
* Orchestra global search box on the page head, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields
|
||||
|
||||
|
||||
* contain error on plugin missing key (plugin dissabled): NOP, fail hard is better than silently, perhaps fail at starttime? apploading machinary
|
||||
|
||||
* contact.alternative_phone on a phone.tooltip, email:to
|
||||
|
@ -223,7 +218,7 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl
|
|||
* autoexpand mailbox.filter according to filtering options
|
||||
|
||||
* allow empty metric pack for default rates? changes on rating algo
|
||||
* IMPORTANT make sure no order is created for mailboxes that include disk? or just don't produce lines with cost == 0 or quantity 0 ?
|
||||
* IMPORTANT make sure no order is created for mailboxes that include disk? or just don't produce lines with cost == 0 or quantity 0 ? maybe minimal quantity for billing? like 0.1 ? or minimal price? per line or per bill?
|
||||
|
||||
* Improve performance of admin change lists with debug toolbar and prefech_related
|
||||
* and miscellaneous.service.name == 'domini-registre'
|
||||
|
@ -231,39 +226,44 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl
|
|||
|
||||
* lines too long on invoice, double lines or cut, and make margin wider
|
||||
* PHP_TIMEOUT env variable in sync with fcgid idle timeout
|
||||
http://foaa.de/old-blog/2010/11/php-apache-and-fastcgi-a-comprehensive-overview/trackback/index.html#pni-top0
|
||||
|
||||
* payment methods icons
|
||||
* use server.name | server.address on python backends, like gitlab instead of settings?
|
||||
* saas change password feature (the only way of re.running a backend)
|
||||
|
||||
* TODO raise404, here and everywhere
|
||||
* display subline links on billlines
|
||||
* update service orders on a celery task?
|
||||
* display subline links on billlines, to show that they exists.
|
||||
* update service orders on a celery task? because it take alot
|
||||
|
||||
*
|
||||
* billline quantity eval('10x100') instead of miningless description '(10*100)'
|
||||
|
||||
* order metric increases inside billed until period
|
||||
* do more test, make sure billed until doesn't get uodated whhen services are billed with les metric, and don't upgrade billed_until when undoing under this circumstances
|
||||
* IMPORTANT do more test, make sure billed until doesn't get uodated whhen services are billed with les metric, and don't upgrade billed_until when undoing under this circumstances
|
||||
* line 513: change threshold and one time service metric change should update last value if not billed, only record for recurring invoicing. postpay services should store the last metric for pricing period.
|
||||
* add ini, end dates on bill lines and breakup quanity into size(defaut:1) and metric
|
||||
* threshold for significative metric accountancy on services.handler
|
||||
|
||||
* move normurlpath to orchestra.utils from websites.utils
|
||||
|
||||
* one time service metric change should update last value, only record for recurring invoicing.
|
||||
|
||||
* write down insights
|
||||
|
||||
* pluggable rate algorithms, with help_text, and change some services to match price
|
||||
|
||||
* translation app, with generates the trans files from models
|
||||
* use english on services defs and so on, an translate them on render time
|
||||
* (miscellaneous.service.ident or '').startswith()
|
||||
|
||||
* websites directives get_location() and use it on last change view validation stage to compare with contents.location and also on the backend ?
|
||||
|
||||
* modeladmin Default filter + search isn't working, prepend filter when searching
|
||||
|
||||
* IMPORTANT do all modles.py TODOs and create migrations for finished apps
|
||||
|
||||
* create service templates based on urlqwargs with the most basic services.
|
||||
|
||||
* Base price: domini propi (all domains) + extra for other domains
|
||||
|
||||
|
||||
Translation
|
||||
-----------
|
||||
|
||||
python manage.py makemessages -l ca --domain database
|
||||
|
||||
mkdir locale
|
||||
django-admin.py makemessages -l ca
|
||||
django-admin.py compilemessages -l ca
|
||||
|
@ -273,5 +273,20 @@ https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#joining-strings-s
|
|||
from django.utils.translation import ugettext
|
||||
from django.utils import translation
|
||||
translation.activate('ca')
|
||||
ugettext("Fuck you")
|
||||
ugettext("Description")
|
||||
|
||||
|
||||
Object = disk*15
|
||||
bscw quota
|
||||
root@web:/home/pangea/bscw/bin ./bsadmin quota report
|
||||
Disk Objects
|
||||
User usage soft hard time usage soft hard time
|
||||
xxx2 -- 0 20M 22M 9 200 300
|
||||
xxxxxxxxxxxxx -- 0 20M 22M 8 200 300
|
||||
xxxxx -- 0 20M 22M 7 200 300
|
||||
xxxxx -- 0 20M 22M 7 200 300
|
||||
|
||||
|
||||
* saas validate_creation generic approach, for all backends. standard output
|
||||
|
||||
* html code x: ×
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', (
|
||||
('INDIVIDUAL', _("Individual")),
|
||||
|
@ -11,7 +13,10 @@ ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', (
|
|||
('STAFF', _("Staff")),
|
||||
))
|
||||
|
||||
ACCOUNTS_DEFAULT_TYPE = getattr(settings, 'ACCOUNTS_DEFAULT_TYPE', 'INDIVIDUAL')
|
||||
|
||||
ACCOUNTS_DEFAULT_TYPE = getattr(settings, 'ACCOUNTS_DEFAULT_TYPE',
|
||||
'INDIVIDUAL'
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_LANGUAGES = getattr(settings, 'ACCOUNTS_LANGUAGES', (
|
||||
|
@ -20,13 +25,18 @@ ACCOUNTS_LANGUAGES = getattr(settings, 'ACCOUNTS_LANGUAGES', (
|
|||
|
||||
|
||||
ACCOUNTS_SYSTEMUSER_MODEL = getattr(settings, 'ACCOUNTS_SYSTEMUSER_MODEL',
|
||||
'systemusers.SystemUser')
|
||||
'systemusers.SystemUser'
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_DEFAULT_LANGUAGE = getattr(settings, 'ACCOUNTS_DEFAULT_LANGUAGE', 'EN')
|
||||
ACCOUNTS_DEFAULT_LANGUAGE = getattr(settings, 'ACCOUNTS_DEFAULT_LANGUAGE',
|
||||
'EN'
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_MAIN_PK = getattr(settings, 'ACCOUNTS_MAIN_PK', 1)
|
||||
ACCOUNTS_MAIN_PK = getattr(settings, 'ACCOUNTS_MAIN_PK',
|
||||
1
|
||||
)
|
||||
|
||||
|
||||
ACCOUNTS_CREATE_RELATED = getattr(settings, 'ACCOUNTS_CREATE_RELATED', (
|
||||
|
@ -42,12 +52,13 @@ ACCOUNTS_CREATE_RELATED = getattr(settings, 'ACCOUNTS_CREATE_RELATED', (
|
|||
('domains.Domain',
|
||||
'name',
|
||||
{
|
||||
'name': '"%s.orchestra.lan" % account.username.replace("_", "-")',
|
||||
'name': '"%s.{}" % account.username.replace("_", "-")'.format(BASE_DOMAIN),
|
||||
},
|
||||
_("Designates whether to creates a related subdomain <username>.orchestra.lan or not."),
|
||||
_("Designates whether to creates a related subdomain <username>.{} or not.".format(BASE_DOMAIN)),
|
||||
),
|
||||
))
|
||||
|
||||
|
||||
ACCOUNTS_SERVICE_REPORT_TEMPLATE = getattr(settings, 'ACCOUNTS_SERVICE_REPORT_TEMPLATE',
|
||||
'admin/accounts/account/service_report.html')
|
||||
'admin/accounts/account/service_report.html'
|
||||
)
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
{% block object-tools-items %}
|
||||
{% if services %}
|
||||
<li><select name="forma" onchange="location = this.options[this.selectedIndex].value;" style="margin: -3px 0 0 0;">
|
||||
<option value="">{% trans "Services:" %}</option>
|
||||
<option selected disabled>{% trans "Services" %}</option>
|
||||
{% for service in services %}
|
||||
<option value="{% url service|admin_urlname:'changelist' %}?account={{ original.pk }}">{{ service.verbose_name_plural|capfirst }}</option>
|
||||
{% endfor %}
|
||||
|
@ -30,7 +30,7 @@
|
|||
{% endif %}
|
||||
{% if accounts %}
|
||||
<li><select name="forma" onchange="location = this.options[this.selectedIndex].value;" style="margin: -3px 4px 0px 4px;">
|
||||
<option value="">{% trans "Accounts:" %}</option>
|
||||
<option selected disabled>{% trans "Accounts" %}</option>
|
||||
{% for account in accounts %}
|
||||
<option value="{% url account|admin_urlname:'changelist' %}?account={{ original.pk }}">{{ account.verbose_name_plural|capfirst }}</option>
|
||||
{% endfor %}
|
||||
|
|
BIN
orchestra/apps/bills/locale/ca/LC_MESSAGES/django.mo
Normal file
BIN
orchestra/apps/bills/locale/ca/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-03-29 10:17+0000\n"
|
||||
"POT-Creation-Date: 2015-03-29 20:21+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -20,11 +20,11 @@ msgstr ""
|
|||
|
||||
#: actions.py:35
|
||||
msgid "Download"
|
||||
msgstr "Blod"
|
||||
msgstr "Descarrega"
|
||||
|
||||
#: actions.py:45
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
msgstr "Vista"
|
||||
|
||||
#: actions.py:53
|
||||
msgid "Selected bills should be in open state"
|
||||
|
@ -62,11 +62,11 @@ msgstr ""
|
|||
msgid "Resend"
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:129 models.py:308
|
||||
#: actions.py:129 models.py:309
|
||||
msgid "Not enough information stored for undoing"
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:132 models.py:310
|
||||
#: actions.py:132 models.py:311
|
||||
msgid "Dates don't match"
|
||||
msgstr ""
|
||||
|
||||
|
@ -92,7 +92,7 @@ msgstr ""
|
|||
|
||||
#: admin.py:69
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
msgstr "Descripció"
|
||||
|
||||
#: admin.py:77
|
||||
msgid "Subtotal"
|
||||
|
@ -115,37 +115,37 @@ msgstr ""
|
|||
msgid "lines"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:152
|
||||
#: admin.py:152 templates/bills/microspective.html:107
|
||||
msgid "total"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:160 models.py:85 models.py:339
|
||||
#: admin.py:160 models.py:85 models.py:340
|
||||
msgid "type"
|
||||
msgstr ""
|
||||
msgstr "tipus"
|
||||
|
||||
#: admin.py:177
|
||||
msgid "Payment"
|
||||
msgstr ""
|
||||
msgstr "Pagament"
|
||||
|
||||
#: filters.py:17
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
msgstr "Tot"
|
||||
|
||||
#: filters.py:18 models.py:75
|
||||
msgid "Invoice"
|
||||
msgstr ""
|
||||
msgstr "Factura"
|
||||
|
||||
#: filters.py:19 models.py:76
|
||||
msgid "Amendment invoice"
|
||||
msgstr ""
|
||||
msgstr "Factura rectificativa"
|
||||
|
||||
#: filters.py:20 models.py:77
|
||||
msgid "Fee"
|
||||
msgstr ""
|
||||
msgstr "Quota de soci"
|
||||
|
||||
#: filters.py:21
|
||||
msgid "Amendment fee"
|
||||
msgstr ""
|
||||
msgstr "Rectificació de quota de soci"
|
||||
|
||||
#: filters.py:22
|
||||
msgid "Pro-forma"
|
||||
|
@ -157,11 +157,11 @@ msgstr ""
|
|||
|
||||
#: filters.py:47
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
msgstr "Si"
|
||||
|
||||
#: filters.py:48
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
msgstr "No"
|
||||
|
||||
#: forms.py:9
|
||||
msgid "Number"
|
||||
|
@ -227,7 +227,7 @@ msgstr ""
|
|||
|
||||
#: models.py:32
|
||||
msgid "VAT number"
|
||||
msgstr ""
|
||||
msgstr "NIF"
|
||||
|
||||
#: models.py:64
|
||||
msgid "Paid"
|
||||
|
@ -285,62 +285,113 @@ msgstr ""
|
|||
msgid "HTML"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:270
|
||||
#: models.py:271
|
||||
msgid "bill"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:271 models.py:336
|
||||
#: models.py:272 models.py:337 templates/bills/microspective.html:73
|
||||
msgid "description"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:272
|
||||
msgid "rate"
|
||||
msgstr ""
|
||||
msgstr "descripció"
|
||||
|
||||
#: models.py:273
|
||||
msgid "quantity"
|
||||
msgstr ""
|
||||
msgid "rate"
|
||||
msgstr "tarifa"
|
||||
|
||||
#: models.py:274
|
||||
msgid "quantity"
|
||||
msgstr "quantitat"
|
||||
|
||||
#: models.py:275 templates/bills/microspective.html:76
|
||||
msgid "subtotal"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:275
|
||||
#: models.py:276
|
||||
msgid "tax"
|
||||
msgstr ""
|
||||
msgstr "impostos"
|
||||
|
||||
#: models.py:281
|
||||
#: models.py:282
|
||||
msgid "Informative link back to the order"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:282
|
||||
#: models.py:283
|
||||
msgid "order billed"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:283
|
||||
#: models.py:284
|
||||
msgid "order billed until"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:284
|
||||
#: models.py:285
|
||||
msgid "created"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:286
|
||||
#: models.py:287
|
||||
msgid "amended line"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:329
|
||||
#: models.py:330
|
||||
msgid "Volume"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:330
|
||||
#: models.py:331
|
||||
msgid "Compensation"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:331
|
||||
#: models.py:332
|
||||
msgid "Other"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:335
|
||||
#: models.py:336
|
||||
msgid "bill line"
|
||||
msgstr ""
|
||||
|
||||
#: templates/bills/microspective.html:74
|
||||
msgid "hrs/qty"
|
||||
msgstr "hrs/quant"
|
||||
|
||||
#: templates/bills/microspective.html:75
|
||||
msgid "rate/price"
|
||||
msgstr "tarifa/preu"
|
||||
|
||||
#: templates/bills/microspective.html:100
|
||||
#: templates/bills/microspective.html:103
|
||||
msgid "VAT"
|
||||
msgstr "IVA"
|
||||
|
||||
#: templates/bills/microspective.html:103
|
||||
msgid "taxes"
|
||||
msgstr ""
|
||||
|
||||
#: templates/bills/microspective.html:119
|
||||
msgid "COMMENTS"
|
||||
msgstr "COMENTARIS"
|
||||
|
||||
#: templates/bills/microspective.html:125
|
||||
msgid "PAYMENT"
|
||||
msgstr "PAGAMENT"
|
||||
|
||||
#: templates/bills/microspective.html:129
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" You can pay our %(type)s by bank transfer. <br>\n"
|
||||
" Please make sure to state your name and the "
|
||||
"%(bill.get_type_display.lower)s number.\n"
|
||||
" Our bank account number is <br>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: templates/bills/microspective.html:138
|
||||
msgid "QUESTIONS"
|
||||
msgstr "PREGUNTES"
|
||||
|
||||
#: templates/bills/microspective.html:139
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" If you have any question about your %(type)s, please\n"
|
||||
" feel free to contact us at your convinience. We will reply as "
|
||||
"soon as we get\n"
|
||||
" your message.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
|
|
@ -3,7 +3,7 @@ from dateutil.relativedelta import relativedelta
|
|||
from django.core.validators import ValidationError, RegexValidator
|
||||
from django.db import models
|
||||
from django.template import loader, Context
|
||||
from django.utils import timezone
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -191,7 +191,7 @@ class Bill(models.Model):
|
|||
self.is_sent = True
|
||||
self.save(update_fields=['is_sent'])
|
||||
|
||||
def render(self, payment=False):
|
||||
def render(self, payment=False, language=None):
|
||||
if payment is False:
|
||||
payment = self.account.paymentsources.get_default()
|
||||
context = Context({
|
||||
|
@ -213,7 +213,8 @@ class Bill(models.Model):
|
|||
template_name = 'BILLS_%s_TEMPLATE' % self.get_type()
|
||||
template = getattr(settings, template_name, settings.BILLS_DEFAULT_TEMPLATE)
|
||||
bill_template = loader.get_template(template)
|
||||
html = bill_template.render(context)
|
||||
with translation.override(language or self.account.language):
|
||||
html = bill_template.render(context)
|
||||
html = html.replace('-pageskip-', '<pdf:nextpage />')
|
||||
return html
|
||||
|
||||
|
|
|
@ -1,66 +1,99 @@
|
|||
from django.conf import settings
|
||||
from django_countries import data
|
||||
|
||||
|
||||
BILLS_NUMBER_LENGTH = getattr(settings, 'BILLS_NUMBER_LENGTH', 4)
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
BILLS_INVOICE_NUMBER_PREFIX = getattr(settings, 'BILLS_INVOICE_NUMBER_PREFIX', 'I')
|
||||
BILLS_NUMBER_LENGTH = getattr(settings, 'BILLS_NUMBER_LENGTH',
|
||||
4
|
||||
)
|
||||
|
||||
|
||||
BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = getattr(settings, 'BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX', 'A')
|
||||
BILLS_INVOICE_NUMBER_PREFIX = getattr(settings, 'BILLS_INVOICE_NUMBER_PREFIX',
|
||||
'I'
|
||||
)
|
||||
|
||||
|
||||
BILLS_FEE_NUMBER_PREFIX = getattr(settings, 'BILLS_FEE_NUMBER_PREFIX', 'F')
|
||||
BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX = getattr(settings, 'BILLS_AMENDMENT_INVOICE_NUMBER_PREFIX',
|
||||
'A'
|
||||
)
|
||||
|
||||
|
||||
BILLS_AMENDMENT_FEE_NUMBER_PREFIX = getattr(settings, 'BILLS_AMENDMENT_FEE_NUMBER_PREFIX', 'B')
|
||||
BILLS_FEE_NUMBER_PREFIX = getattr(settings, 'BILLS_FEE_NUMBER_PREFIX',
|
||||
'F'
|
||||
)
|
||||
|
||||
|
||||
BILLS_PROFORMA_NUMBER_PREFIX = getattr(settings, 'BILLS_PROFORMA_NUMBER_PREFIX', 'P')
|
||||
BILLS_AMENDMENT_FEE_NUMBER_PREFIX = getattr(settings, 'BILLS_AMENDMENT_FEE_NUMBER_PREFIX',
|
||||
'B'
|
||||
)
|
||||
|
||||
|
||||
BILLS_PROFORMA_NUMBER_PREFIX = getattr(settings, 'BILLS_PROFORMA_NUMBER_PREFIX',
|
||||
'P'
|
||||
)
|
||||
|
||||
|
||||
BILLS_DEFAULT_TEMPLATE = getattr(settings, 'BILLS_DEFAULT_TEMPLATE',
|
||||
'bills/microspective.html')
|
||||
'bills/microspective.html'
|
||||
)
|
||||
|
||||
|
||||
BILLS_FEE_TEMPLATE = getattr(settings, 'BILLS_FEE_TEMPLATE',
|
||||
'bills/microspective-fee.html')
|
||||
'bills/microspective-fee.html'
|
||||
)
|
||||
|
||||
|
||||
BILLS_PROFORMA_TEMPLATE = getattr(settings, 'BILLS_PROFORMA_TEMPLATE',
|
||||
'bills/microspective-proforma.html')
|
||||
'bills/microspective-proforma.html'
|
||||
)
|
||||
|
||||
|
||||
BILLS_CURRENCY = getattr(settings, 'BILLS_CURRENCY', 'euro')
|
||||
BILLS_CURRENCY = getattr(settings, 'BILLS_CURRENCY',
|
||||
'euro'
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_PHONE = getattr(settings, 'BILLS_SELLER_PHONE', '111-112-11-222')
|
||||
BILLS_SELLER_PHONE = getattr(settings, 'BILLS_SELLER_PHONE',
|
||||
'111-112-11-222'
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_EMAIL = getattr(settings, 'BILLS_SELLER_EMAIL', 'sales@orchestra.lan')
|
||||
BILLS_SELLER_EMAIL = getattr(settings, 'BILLS_SELLER_EMAIL',
|
||||
'sales@{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_WEBSITE = getattr(settings, 'BILLS_SELLER_WEBSITE', 'www.orchestra.lan')
|
||||
BILLS_SELLER_WEBSITE = getattr(settings, 'BILLS_SELLER_WEBSITE',
|
||||
'www.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
BILLS_SELLER_BANK_ACCOUNT = getattr(settings, 'BILLS_SELLER_BANK_ACCOUNT',
|
||||
'0000 0000 00 00000000 (Orchestra Bank)')
|
||||
'0000 0000 00 00000000 (Orchestra Bank)'
|
||||
)
|
||||
|
||||
|
||||
BILLS_EMAIL_NOTIFICATION_TEMPLATE = getattr(settings, 'BILLS_EMAIL_NOTIFICATION_TEMPLATE',
|
||||
'bills/bill-notification.email')
|
||||
'bills/bill-notification.email'
|
||||
)
|
||||
|
||||
|
||||
BILLS_ORDER_MODEL = getattr(settings, 'BILLS_ORDER_MODEL', 'orders.Order')
|
||||
BILLS_ORDER_MODEL = getattr(settings, 'BILLS_ORDER_MODEL',
|
||||
'orders.Order'
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_DEFAULT_CITY = getattr(settings, 'BILLS_CONTACT_DEFAULT_CITY', 'Barcelona')
|
||||
BILLS_CONTACT_DEFAULT_CITY = getattr(settings, 'BILLS_CONTACT_DEFAULT_CITY',
|
||||
'Barcelona'
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_COUNTRIES = getattr(settings, 'BILLS_CONTACT_COUNTRIES', ((k,v) for k,v in data.COUNTRIES.iteritems()))
|
||||
|
||||
|
||||
BILLS_CONTACT_DEFAULT_COUNTRY = getattr(settings, 'BILLS_CONTACT_DEFAULT_COUNTRY', 'ES')
|
||||
BILLS_CONTACT_COUNTRIES = getattr(settings, 'BILLS_CONTACT_COUNTRIES',
|
||||
((k,v) for k,v in data.COUNTRIES.iteritems())
|
||||
)
|
||||
|
||||
|
||||
BILLS_CONTACT_DEFAULT_COUNTRY = getattr(settings, 'BILLS_CONTACT_DEFAULT_COUNTRY',
|
||||
'ES'
|
||||
)
|
||||
|
|
|
@ -123,7 +123,7 @@ div#simple table{
|
|||
<table width="100%">
|
||||
<tr>
|
||||
<th width="5%">ID</th>
|
||||
<th width="65%">Description</th>
|
||||
<th width="65%">{% trans Description %}</th>
|
||||
<th width="20%">Amount</th>
|
||||
<th width="10%">Price</th>
|
||||
</tr>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
body {
|
||||
/* max-width: 650px;*/
|
||||
max-width: 670px;
|
||||
max-width: 820px;
|
||||
margin: 40 auto !important;
|
||||
/* margin-bottom: 30 !important;*/
|
||||
float: none !important;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% extends 'bills/base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head %}
|
||||
<style type="text/css">
|
||||
|
@ -69,10 +70,10 @@
|
|||
{% block content %}
|
||||
<div id="lines">
|
||||
<span class="title column-id">id</span>
|
||||
<span class="title column-description">description</span>
|
||||
<span class="title column-quantity">hrs/qty</span>
|
||||
<span class="title column-rate">rate/price</span>
|
||||
<span class="title column-subtotal">subtotal</span>
|
||||
<span class="title column-description">{% trans "description" %}</span>
|
||||
<span class="title column-quantity">{% trans "hrs/qty" %}</span>
|
||||
<span class="title column-rate">{% trans "rate/price" %}</span>
|
||||
<span class="title column-subtotal">{% trans "subtotal" %}</span>
|
||||
<br>
|
||||
{% for line in lines %}
|
||||
{% with sublines=line.sublines.all %}
|
||||
|
@ -96,14 +97,14 @@
|
|||
<div id="totals">
|
||||
<br> <br>
|
||||
{% for tax, subtotal in bill.get_subtotals.iteritems %}
|
||||
<span class="subtotal column-title">subtotal {{ tax }}% VAT</span>
|
||||
<span class="subtotal column-title">subtotal {{ tax }}% {% trans "VAT" %}</span>
|
||||
<span class="subtotal column-value">{{ subtotal | first }} &{{ currency.lower }};</span>
|
||||
<br>
|
||||
<span class="tax column-title">taxes {{ tax }}% VAT</span>
|
||||
<span class="tax column-title">{% trans "taxes" %} {{ tax }}% {% trans "VAT" %}</span>
|
||||
<span class="tax column-value">{{ subtotal | last }} &{{ currency.lower }};</span>
|
||||
<br>
|
||||
{% endfor %}
|
||||
<span class="total column-title">total</span>
|
||||
<span class="total column-title">{% trans "total" %}</span>
|
||||
<span class="total column-value">{{ bill.get_total }} &{{ currency.lower }};</span>
|
||||
<br>
|
||||
</div>
|
||||
|
@ -115,26 +116,31 @@
|
|||
<div id="footer-column-1">
|
||||
<div id="comments">
|
||||
{% if bill.comments %}
|
||||
<span class="title">COMMENTS</span> {{ bill.comments|linebreaksbr }}
|
||||
<span class="title">{% trans "COMMENTS" %}</span> {{ bill.comments|linebreaksbr }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer-column-2">
|
||||
<div id="payment">
|
||||
<span class="title">PAYMENT</span>
|
||||
<span class="title">{% trans "PAYMENT" %}</span>
|
||||
{% if payment.message %}
|
||||
{{ payment.message | safe }}
|
||||
{% else %}
|
||||
You can pay our {{ bill.get_type_display.lower }} by bank transfer. <br>
|
||||
{% blocktrans with type=bill.get_type_display.lower %}
|
||||
You can pay our {{ type }} by bank transfer. <br>
|
||||
Please make sure to state your name and the {{ bill.get_type_display.lower}} number.
|
||||
Our bank account number is <br>
|
||||
{% endblocktrans %}
|
||||
<strong>{{ seller_info.bank_account }}</strong>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="questions">
|
||||
<span class="title">QUESTIONS</span> If you have any question about your {{ bill.get_type_display.lower}}, please
|
||||
<span class="title">{% trans "QUESTIONS" %}</span>
|
||||
{% blocktrans with type=bill.get_type_display.lower %}
|
||||
If you have any question about your {{ type }}, please
|
||||
feel free to contact us at your convinience. We will reply as soon as we get
|
||||
your message.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,9 @@ CONTACTS_DEFAULT_EMAIL_USAGES = getattr(settings, 'CONTACTS_DEFAULT_EMAIL_USAGES
|
|||
))
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY', 'Barcelona')
|
||||
CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY',
|
||||
'Barcelona'
|
||||
)
|
||||
|
||||
|
||||
CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', (
|
||||
|
@ -20,4 +22,6 @@ CONTACTS_COUNTRIES = getattr(settings, 'CONTACTS_COUNTRIES', (
|
|||
))
|
||||
|
||||
|
||||
CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY', 'ES')
|
||||
CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY',
|
||||
'ES'
|
||||
)
|
||||
|
|
|
@ -7,7 +7,11 @@ DATABASES_TYPE_CHOICES = getattr(settings, 'DATABASES_TYPE_CHOICES', (
|
|||
))
|
||||
|
||||
|
||||
DATABASES_DEFAULT_TYPE = getattr(settings, 'DATABASES_DEFAULT_TYPE', 'mysql')
|
||||
DATABASES_DEFAULT_TYPE = getattr(settings, 'DATABASES_DEFAULT_TYPE',
|
||||
'mysql'
|
||||
)
|
||||
|
||||
|
||||
DATABASES_DEFAULT_HOST = getattr(settings, 'DATABASES_DEFAULT_HOST', 'localhost')
|
||||
DATABASES_DEFAULT_HOST = getattr(settings, 'DATABASES_DEFAULT_HOST',
|
||||
'localhost'
|
||||
)
|
||||
|
|
|
@ -1,57 +1,83 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_NAME_SERVER = getattr(settings, 'DOMAINS_DEFAULT_NAME_SERVER',
|
||||
'ns.orchestra.lan')
|
||||
'ns.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_HOSTMASTER = getattr(settings, 'DOMAINS_DEFAULT_HOSTMASTER',
|
||||
'hostmaster@orchestra.lan')
|
||||
'hostmaster@{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_TTL = getattr(settings, 'DOMAINS_DEFAULT_TTL', '1h')
|
||||
DOMAINS_DEFAULT_TTL = getattr(settings, 'DOMAINS_DEFAULT_TTL',
|
||||
'1h'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_REFRESH = getattr(settings, 'DOMAINS_DEFAULT_REFRESH', '1d')
|
||||
DOMAINS_DEFAULT_REFRESH = getattr(settings, 'DOMAINS_DEFAULT_REFRESH',
|
||||
'1d'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_RETRY = getattr(settings, 'DOMAINS_DEFAULT_RETRY', '2h')
|
||||
DOMAINS_DEFAULT_RETRY = getattr(settings, 'DOMAINS_DEFAULT_RETRY',
|
||||
'2h'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_EXPIRATION = getattr(settings, 'DOMAINS_DEFAULT_EXPIRATION', '4w')
|
||||
DOMAINS_DEFAULT_EXPIRATION = getattr(settings, 'DOMAINS_DEFAULT_EXPIRATION',
|
||||
'4w'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_MIN_CACHING_TIME = getattr(settings, 'DOMAINS_DEFAULT_MIN_CACHING_TIME', '1h')
|
||||
DOMAINS_DEFAULT_MIN_CACHING_TIME = getattr(settings, 'DOMAINS_DEFAULT_MIN_CACHING_TIME',
|
||||
'1h'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_ZONE_PATH = getattr(settings, 'DOMAINS_ZONE_PATH', '/etc/bind/master/%(name)s')
|
||||
DOMAINS_ZONE_PATH = getattr(settings, 'DOMAINS_ZONE_PATH',
|
||||
'/etc/bind/master/%(name)s'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_MASTERS_PATH = getattr(settings, 'DOMAINS_MASTERS_PATH', '/etc/bind/named.conf.local')
|
||||
DOMAINS_MASTERS_PATH = getattr(settings, 'DOMAINS_MASTERS_PATH',
|
||||
'/etc/bind/named.conf.local'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_SLAVES_PATH = getattr(settings, 'DOMAINS_SLAVES_PATH', '/etc/bind/named.conf.local')
|
||||
DOMAINS_SLAVES_PATH = getattr(settings, 'DOMAINS_SLAVES_PATH',
|
||||
'/etc/bind/named.conf.local'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_CHECKZONE_BIN_PATH = getattr(settings, 'DOMAINS_CHECKZONE_BIN_PATH',
|
||||
'/usr/sbin/named-checkzone -i local -k fail -n fail')
|
||||
'/usr/sbin/named-checkzone -i local -k fail -n fail'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_CHECKZONE_PATH = getattr(settings, 'DOMAINS_CHECKZONE_PATH', '/dev/shm')
|
||||
# Used for creating temporary zone files used for validation
|
||||
DOMAINS_ZONE_VALIDATION_TMP_DIR = getattr(settings, 'DOMAINS_ZONE_VALIDATION_TMP_DIR',
|
||||
'/dev/shm'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_A = getattr(settings, 'DOMAINS_DEFAULT_A', '10.0.3.13')
|
||||
DOMAINS_DEFAULT_A = getattr(settings, 'DOMAINS_DEFAULT_A',
|
||||
'10.0.3.13'
|
||||
)
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_MX = getattr(settings, 'DOMAINS_DEFAULT_MX', (
|
||||
'10 mail.orchestra.lan.',
|
||||
'10 mail2.orchestra.lan.',
|
||||
'10 mail.{}.'.format(BASE_DOMAIN),
|
||||
'10 mail2.{}.'.format(BASE_DOMAIN),
|
||||
))
|
||||
|
||||
|
||||
DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', (
|
||||
'ns1.orchestra.lan.',
|
||||
'ns2.orchestra.lan.',
|
||||
'ns1.{}.'.format(BASE_DOMAIN),
|
||||
'ns2.{}.'.format(BASE_DOMAIN),
|
||||
))
|
||||
|
||||
|
||||
|
@ -61,5 +87,5 @@ DOMAINS_FORBIDDEN = getattr(settings, 'DOMAINS_FORBIDDEN',
|
|||
# wget http://s3.amazonaws.com/alexa-static/top-1m.csv.zip -O /tmp/top-1m.csv.zip
|
||||
# unzip -p /tmp/top-1m.csv.zip | head -n 5000 | sed "s/^.*,//" > forbidden_domains.list
|
||||
|
||||
# '%(site_root)s/forbidden_domains.list')
|
||||
# '%(site_dir)s/forbidden_domains.list')
|
||||
'')
|
||||
|
|
|
@ -13,7 +13,7 @@ from . import settings
|
|||
|
||||
def validate_allowed_domain(value):
|
||||
context = {
|
||||
'site_root': paths.get_site_root()
|
||||
'site_dir': paths.get_site_dir()
|
||||
}
|
||||
fname = settings.DOMAINS_FORBIDDEN
|
||||
if fname:
|
||||
|
@ -108,12 +108,15 @@ def validate_soa_record(value):
|
|||
def validate_zone(zone):
|
||||
""" Ultimate zone file validation using named-checkzone """
|
||||
zone_name = zone.split()[0][:-1]
|
||||
path = os.path.join(settings.DOMAINS_CHECKZONE_PATH, zone_name)
|
||||
with open(path, 'wb') as f:
|
||||
f.write(zone)
|
||||
# Don't use /dev/stdin becuase the 'argument list is too long' error
|
||||
zone_path = os.path.join(settings.DOMAINS_ZONE_VALIDATION_TMP_DIR, zone_name)
|
||||
checkzone = settings.DOMAINS_CHECKZONE_BIN_PATH
|
||||
check = run(' '.join([checkzone, zone_name, path]), error_codes=[0,1], display=False)
|
||||
try:
|
||||
with open(zone_path, 'wb') as f:
|
||||
f.write(zone_path)
|
||||
# Don't use /dev/stdin becuase the 'argument list is too long' error
|
||||
check = run(' '.join([checkzone, zone_name, zone_path]), error_codes=[0,1], display=False)
|
||||
finally:
|
||||
os.unlink(zone_path)
|
||||
if check.return_code == 1:
|
||||
errors = re.compile(r'zone.*: (.*)').findall(check.stdout)[:-1]
|
||||
raise ValidationError(', '.join(errors))
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
ISSUES_SUPPORT_EMAILS = getattr(settings, 'ISSUES_SUPPORT_EMAILS', [])
|
||||
ISSUES_SUPPORT_EMAILS = getattr(settings, 'ISSUES_SUPPORT_EMAILS', [
|
||||
])
|
||||
|
||||
|
||||
ISSUES_NOTIFY_SUPERUSERS = getattr(settings, 'ISSUES_NOTIFY_SUPERUSERS', True)
|
||||
ISSUES_NOTIFY_SUPERUSERS = getattr(settings, 'ISSUES_NOTIFY_SUPERUSERS',
|
||||
True
|
||||
)
|
||||
|
|
|
@ -1,24 +1,38 @@
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL', 'domains.Domain')
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
LISTS_DEFAULT_DOMAIN = getattr(settings, 'LIST_DEFAULT_DOMAIN', 'lists.orchestra.lan')
|
||||
LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
)
|
||||
|
||||
|
||||
LISTS_LIST_URL = getattr(settings, 'LISTS_LIST_URL', 'https://lists.orchestra.lan/mailman/listinfo/%(name)s')
|
||||
LISTS_DEFAULT_DOMAIN = getattr(settings, 'LIST_DEFAULT_DOMAIN',
|
||||
'lists.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
LISTS_LIST_URL = getattr(settings, 'LISTS_LIST_URL',
|
||||
'https://lists.{}/mailman/listinfo/%(name)s'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
LISTS_MAILMAN_POST_LOG_PATH = getattr(settings, 'LISTS_MAILMAN_POST_LOG_PATH',
|
||||
'/var/log/mailman/post')
|
||||
'/var/log/mailman/post'
|
||||
)
|
||||
|
||||
|
||||
LISTS_MAILMAN_ROOT_PATH = getattr(settings, 'LISTS_MAILMAN_ROOT_PATH',
|
||||
'/var/lib/mailman/')
|
||||
'/var/lib/mailman/'
|
||||
)
|
||||
|
||||
|
||||
LISTS_VIRTUAL_ALIAS_PATH = getattr(settings, 'LISTS_VIRTUAL_ALIAS_PATH',
|
||||
'/etc/postfix/mailman_virtual_aliases')
|
||||
'/etc/postfix/mailman_virtual_aliases'
|
||||
)
|
||||
|
||||
|
||||
LISTS_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
'/etc/postfix/mailman_virtual_domains')
|
||||
'/etc/postfix/mailman_virtual_domains'
|
||||
)
|
||||
|
|
|
@ -4,42 +4,57 @@ import textwrap
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
MAILBOXES_DOMAIN_MODEL = getattr(settings, 'MAILBOXES_DOMAIN_MODEL', 'domains.Domain')
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
MAILBOXES_HOME = getattr(settings, 'MAILBOXES_HOME', '/home/./%(name)s/')
|
||||
MAILBOXES_DOMAIN_MODEL = getattr(settings, 'MAILBOXES_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_HOME = getattr(settings, 'MAILBOXES_HOME',
|
||||
'/home/%(name)s/'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVE_PATH = getattr(settings, 'MAILBOXES_SIEVE_PATH',
|
||||
os.path.join(MAILBOXES_HOME, 'Maildir/sieve/orchestra.sieve'))
|
||||
os.path.join(MAILBOXES_HOME, 'Maildir/sieve/orchestra.sieve')
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVETEST_PATH = getattr(settings, 'MAILBOXES_SIEVETEST_PATH', '/dev/shm')
|
||||
MAILBOXES_SIEVETEST_PATH = getattr(settings, 'MAILBOXES_SIEVETEST_PATH',
|
||||
'/dev/shm'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_SIEVETEST_BIN_PATH = getattr(settings, 'MAILBOXES_SIEVETEST_BIN_PATH',
|
||||
'%(orchestra_root)s/bin/sieve-test')
|
||||
'%(orchestra_root)s/bin/sieve-test'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_MAILBOX_MAPS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_MAILBOX_MAPS_PATH',
|
||||
'/etc/postfix/virtual_mailboxes')
|
||||
|
||||
'/etc/postfix/virtual_mailboxes'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_ALIAS_MAPS_PATH',
|
||||
'/etc/postfix/virtual_aliases')
|
||||
'/etc/postfix/virtual_aliases'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH',
|
||||
'/etc/postfix/virtual_domains')
|
||||
'/etc/postfix/virtual_domains'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_VIRTUAL_MAILBOX_DEFAULT_DOMAIN = getattr(settings, 'MAILBOXES_VIRTUAL_MAILBOX_DEFAULT_DOMAIN',
|
||||
'orchestra.lan')
|
||||
BASE_DOMAIN
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_PASSWD_PATH = getattr(settings, 'MAILBOXES_PASSWD_PATH',
|
||||
'/etc/dovecot/passwd')
|
||||
'/etc/dovecot/passwd'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MAILBOX_FILTERINGS = getattr(settings, 'MAILBOXES_MAILBOX_FILTERINGS', {
|
||||
|
@ -62,11 +77,15 @@ MAILBOXES_MAILBOX_FILTERINGS = getattr(settings, 'MAILBOXES_MAILBOX_FILTERINGS',
|
|||
|
||||
|
||||
MAILBOXES_MAILBOX_DEFAULT_FILTERING = getattr(settings, 'MAILBOXES_MAILBOX_DEFAULT_FILTERING',
|
||||
'REDIRECT')
|
||||
'REDIRECT'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_MAILDIRSIZE_PATH = getattr(settings, 'MAILBOXES_MAILDIRSIZE_PATH', '%(home)s/Maildir/maildirsize')
|
||||
MAILBOXES_MAILDIRSIZE_PATH = getattr(settings, 'MAILBOXES_MAILDIRSIZE_PATH',
|
||||
'%(home)s/Maildir/maildirsize'
|
||||
)
|
||||
|
||||
|
||||
MAILBOXES_LOCAL_ADDRESS_DOMAIN = getattr(settings, 'MAILBOXES_LOCAL_ADDRESS_DOMAIN',
|
||||
'orchestra.lan')
|
||||
BASE_DOMAIN
|
||||
)
|
||||
|
|
|
@ -55,7 +55,7 @@ def validate_sieve(value):
|
|||
with open(path, 'wb') as f:
|
||||
f.write(value)
|
||||
context = {
|
||||
'orchestra_root': paths.get_orchestra_root()
|
||||
'orchestra_root': paths.get_orchestra_dir()
|
||||
}
|
||||
sievetest = settings.MAILBOXES_SIEVETEST_BIN_PATH % context
|
||||
try:
|
||||
|
|
|
@ -72,7 +72,7 @@ class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelA
|
|||
|
||||
def get_service(self, obj):
|
||||
if obj is None:
|
||||
return self.plugin.get_plugin(self.plugin_value).related_instance
|
||||
return self.plugin.get(self.plugin_value).related_instance
|
||||
else:
|
||||
return obj.service
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
MISCELLANEOUS_IDENTIFIER_VALIDATORS = getattr(settings, 'MISCELLANEOUS_IDENTIFIER_VALIDATORS', {})
|
||||
# MISCELLANEOUS_IDENTIFIER_VALIDATORS = { miscservice__name: validator_function }
|
||||
MISCELLANEOUS_IDENTIFIER_VALIDATORS = getattr(settings, 'MISCELLANEOUS_IDENTIFIER_VALIDATORS', {
|
||||
# <miscservice__name>: <validator_function>
|
||||
})
|
||||
|
|
|
@ -32,7 +32,7 @@ class RouteAdmin(admin.ModelAdmin):
|
|||
|
||||
BACKEND_HELP_TEXT = {
|
||||
backend: "This backend operates over '%s'" % ServiceBackend.get_backend(backend).model
|
||||
for backend, __ in ServiceBackend.get_plugin_choices()
|
||||
for backend, __ in ServiceBackend.get_choices()
|
||||
}
|
||||
DEFAULT_MATCH = {
|
||||
backend.get_name(): backend.default_route_match for backend in ServiceBackend.get_backends(active=False)
|
||||
|
|
|
@ -119,7 +119,7 @@ class ServiceBackend(plugins.Plugin):
|
|||
|
||||
@classmethod
|
||||
def get_backend(cls, name):
|
||||
return cls.get_plugin(name)
|
||||
return cls.get(name)
|
||||
|
||||
@classmethod
|
||||
def model_class(cls):
|
||||
|
|
|
@ -170,7 +170,7 @@ class Route(models.Model):
|
|||
Defines the routing that determine in which server a backend is executed
|
||||
"""
|
||||
backend = models.CharField(_("backend"), max_length=256,
|
||||
choices=ServiceBackend.get_plugin_choices())
|
||||
choices=ServiceBackend.get_choices())
|
||||
host = models.ForeignKey(Server, verbose_name=_("host"))
|
||||
match = models.CharField(_("match"), max_length=256, blank=True, default='True',
|
||||
help_text=_("Python expression used for selecting the targe host, "
|
||||
|
|
|
@ -24,7 +24,7 @@ class RouterTests(BaseTestCase):
|
|||
def save(self, instance):
|
||||
pass
|
||||
|
||||
choices = backends.ServiceBackend.get_plugin_choices()
|
||||
choices = backends.ServiceBackend.get_choices()
|
||||
Route._meta.get_field_by_name('backend')[0]._choices = choices
|
||||
backend = TestBackend.get_name()
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ class BillsBackend(object):
|
|||
if service.metric and service.billing_period != service.NEVER and service.pricing_period == service.NEVER:
|
||||
metric = format(line.metric, '.2f').rstrip('0').rstrip('.')
|
||||
size = format(line.size, '.2f').rstrip('0').rstrip('.')
|
||||
description += " (%s*%s)" % (metric, size)
|
||||
description += " (%sx%s)" % (metric, size)
|
||||
return description
|
||||
|
||||
def create_sublines(self, line, discounts):
|
||||
|
|
|
@ -237,6 +237,7 @@ class Order(models.Model):
|
|||
|
||||
|
||||
class MetricStorage(models.Model):
|
||||
""" Stores metric state for future billing """
|
||||
order = models.ForeignKey(Order, verbose_name=_("order"), related_name='metrics')
|
||||
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
|
||||
created_on = models.DateField(_("created"), auto_now_add=True)
|
||||
|
@ -253,16 +254,16 @@ class MetricStorage(models.Model):
|
|||
def store(cls, order, value):
|
||||
now = timezone.now()
|
||||
try:
|
||||
metric = cls.objects.filter(order=order).latest()
|
||||
last = cls.objects.filter(order=order).latest()
|
||||
except cls.DoesNotExist:
|
||||
cls.objects.create(order=order, value=value, updated_on=now)
|
||||
else:
|
||||
threshold = decimal.Decimal(settings.ORDERS_METRIC_THRESHOLD)
|
||||
if metric.value*(1+threshold) > value or metric.value*threshold < value:
|
||||
error = decimal.Decimal(settings.ORDERS_METRIC_ERROR)
|
||||
if last.value*(1+error) > value or last.value*error < value:
|
||||
cls.objects.create(order=order, value=value, updated_on=now)
|
||||
else:
|
||||
metric.updated_on = now
|
||||
metric.save(update_fields=['updated_on'])
|
||||
last.updated_on = now
|
||||
last.save(update_fields=['updated_on'])
|
||||
|
||||
|
||||
accounts.register(Order)
|
||||
|
|
|
@ -3,11 +3,14 @@ from django.conf import settings
|
|||
|
||||
# Pluggable backend for bill generation.
|
||||
ORDERS_BILLING_BACKEND = getattr(settings, 'ORDERS_BILLING_BACKEND',
|
||||
'orchestra.apps.orders.billing.BillsBackend')
|
||||
'orchestra.apps.orders.billing.BillsBackend'
|
||||
)
|
||||
|
||||
|
||||
# Pluggable service class
|
||||
ORDERS_SERVICE_MODEL = getattr(settings, 'ORDERS_SERVICE_MODEL', 'services.Service')
|
||||
ORDERS_SERVICE_MODEL = getattr(settings, 'ORDERS_SERVICE_MODEL',
|
||||
'services.Service'
|
||||
)
|
||||
|
||||
|
||||
# Prevent inspecting these apps for service accounting
|
||||
|
@ -26,4 +29,6 @@ ORDERS_EXCLUDED_APPS = getattr(settings, 'ORDERS_EXCLUDED_APPS', (
|
|||
|
||||
# Only account for significative changes
|
||||
# metric_storage new value: lastvalue*(1+threshold) > currentvalue or lastvalue*threshold < currentvalue
|
||||
ORDERS_METRIC_THRESHOLD = getattr(settings, 'ORDERS_METRIC_THRESHOLD', 0.4)
|
||||
ORDERS_METRIC_ERROR = getattr(settings, 'ORDERS_METRIC_ERROR',
|
||||
0.01
|
||||
)
|
||||
|
|
|
@ -23,7 +23,7 @@ def process_transactions(modeladmin, request, queryset):
|
|||
return
|
||||
for method, transactions in queryset.group_by('source__method').iteritems():
|
||||
if method is not None:
|
||||
method = PaymentMethod.get_plugin(method)
|
||||
method = PaymentMethod.get(method)
|
||||
procs = method.process(transactions)
|
||||
processes += procs
|
||||
for trans in transactions:
|
||||
|
|
|
@ -20,7 +20,7 @@ class PaymentSource(models.Model):
|
|||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||
related_name='paymentsources')
|
||||
method = models.CharField(_("method"), max_length=32,
|
||||
choices=PaymentMethod.get_plugin_choices())
|
||||
choices=PaymentMethod.get_choices())
|
||||
data = JSONField(_("data"), default={})
|
||||
is_active = models.BooleanField(_("active"), default=True)
|
||||
|
||||
|
@ -31,7 +31,7 @@ class PaymentSource(models.Model):
|
|||
|
||||
@cached_property
|
||||
def method_class(self):
|
||||
return PaymentMethod.get_plugin(self.method)
|
||||
return PaymentMethod.get(self.method)
|
||||
|
||||
@cached_property
|
||||
def method_instance(self):
|
||||
|
|
|
@ -12,7 +12,7 @@ class PaymentSourceSerializer(AccountSerializerMixin, serializers.HyperlinkedMod
|
|||
fields = ('url', 'method', 'data', 'is_active')
|
||||
|
||||
def validate_data(self, attrs, source):
|
||||
plugin = PaymentMethod.get_plugin(attrs['method'])
|
||||
plugin = PaymentMethod.get(attrs['method'])
|
||||
serializer_class = plugin().get_serializer()
|
||||
serializer = serializer_class(data=attrs[source])
|
||||
if not serializer.is_valid():
|
||||
|
@ -23,7 +23,7 @@ class PaymentSourceSerializer(AccountSerializerMixin, serializers.HyperlinkedMod
|
|||
if not obj:
|
||||
return {}
|
||||
if obj.method:
|
||||
plugin = PaymentMethod.get_plugin(obj.method)
|
||||
plugin = PaymentMethod.get(obj.method)
|
||||
serializer_class = plugin().get_serializer()
|
||||
return serializer_class().to_native(obj.data)
|
||||
return obj.data
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
from django.conf import settings
|
||||
|
||||
|
||||
PAYMENT_CURRENCY = getattr(settings, 'PAYMENT_CURRENCY', 'Eur')
|
||||
PAYMENT_CURRENCY = getattr(settings, 'PAYMENT_CURRENCY',
|
||||
'Eur'
|
||||
)
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_NAME = getattr(settings, 'PAYMENTS_DD_CREDITOR_NAME',
|
||||
'Orchestra')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_IBAN = getattr(settings, 'PAYMENTS_DD_CREDITOR_IBAN',
|
||||
'IE98BOFI90393912121212')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_BIC = getattr(settings, 'PAYMENTS_DD_CREDITOR_BIC',
|
||||
'BOFIIE2D')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_AT02_ID = getattr(settings, 'PAYMENTS_DD_CREDITOR_AT02_ID',
|
||||
'InvalidAT02ID')
|
||||
|
||||
|
|
|
@ -84,8 +84,15 @@ class Rate(models.Model):
|
|||
return "{}-{}".format(str(self.price), self.quantity)
|
||||
|
||||
@classmethod
|
||||
def get_methods(self):
|
||||
return self.RATE_METHODS
|
||||
def get_methods(cls):
|
||||
return cls.RATE_METHODS
|
||||
|
||||
@classmethod
|
||||
def get_choices(cls):
|
||||
choices = []
|
||||
for name, method in cls.RATE_METHODS.iteritems():
|
||||
choices.append((name, method.verbose_name))
|
||||
return choices
|
||||
|
||||
|
||||
accounts.register(ContractedPlan)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import sys
|
||||
|
||||
from django.utils.translation import string_concat, ugettext_lazy as _
|
||||
|
||||
from orchestra.utils.python import AttrDict
|
||||
|
||||
|
||||
|
@ -117,6 +119,8 @@ def step_price(rates, metric):
|
|||
ix += 1
|
||||
minimal = min(minimal, (value, result), key=lambda v: v[0])
|
||||
return minimal[1]
|
||||
step_price.verbose_name = _("Step price")
|
||||
step_price.help_text = _("All price rates with a lower metric are applied.")
|
||||
|
||||
|
||||
def match_price(rates, metric):
|
||||
|
@ -144,3 +148,5 @@ def match_price(rates, metric):
|
|||
'price': candidates[0].price,
|
||||
})]
|
||||
return None
|
||||
match_price.verbose_name = _("Match price")
|
||||
match_price.help_text = _("Only the rate with inmediate inferior metric is applied.")
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import datetime
|
||||
|
||||
|
||||
def compute_resource_usage(data):
|
||||
""" Computes MonitorData.used based on related monitors """
|
||||
resource = data.resource
|
||||
result = 0
|
||||
has_result = False
|
||||
today = datetime.date.today()
|
||||
for dataset in data.get_monitor_datasets():
|
||||
if resource.period == resource.MONTHLY_AVG:
|
||||
last = dataset.latest()
|
||||
epoch = datetime(
|
||||
year=today.year,
|
||||
month=today.month,
|
||||
day=1,
|
||||
tzinfo=timezone.utc
|
||||
)
|
||||
total = (last.created_at-epoch).total_seconds()
|
||||
ini = epoch
|
||||
for data in dataset:
|
||||
slot = (data.created_at-ini).total_seconds()
|
||||
result += data.value * slot/total
|
||||
ini = data.created_at
|
||||
elif resource.period in (resource.MONTHLY_SUM, resource.LAST):
|
||||
# FIXME Aggregation of 0s returns None! django bug?
|
||||
# value = dataset.aggregate(models.Sum('value'))['value__sum']
|
||||
values = dataset.values_list('value', flat=True)
|
||||
if values:
|
||||
has_result = True
|
||||
result += sum(values)
|
||||
else:
|
||||
raise NotImplementedError("%s support not implemented" % data.period)
|
||||
return float(result)/resource.get_scale() if has_result else None
|
89
orchestra/apps/resources/methods.py
Normal file
89
orchestra/apps/resources/methods.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
import datetime
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra import plugins
|
||||
|
||||
|
||||
class DataMethod(plugins.Plugin):
|
||||
""" filters and computes dataset usage """
|
||||
__metaclass__ = plugins.PluginMount
|
||||
|
||||
def filter(self, dataset):
|
||||
""" Filter the dataset to get the relevant data according to the period """
|
||||
raise NotImplementedError
|
||||
|
||||
def compute_usage(self, dataset):
|
||||
""" given a dataset computes its usage according to the method (avg, sum, ...) """
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Last(DataMethod):
|
||||
name = 'last'
|
||||
verbose_name = _("Last")
|
||||
|
||||
def filter(self, dataset):
|
||||
try:
|
||||
return dataset.order_by('object_id', '-id').distinct('object_id')
|
||||
except MonitorData.DoesNotExist:
|
||||
return dataset.none()
|
||||
|
||||
def compute_usage(self, dataset):
|
||||
# FIXME Aggregation of 0s returns None! django bug?
|
||||
# value = dataset.aggregate(models.Sum('value'))['value__sum']
|
||||
values = dataset.values_list('value', flat=True)
|
||||
if values:
|
||||
return sum(values)
|
||||
return None
|
||||
|
||||
|
||||
class MonthlySum(Last):
|
||||
name = 'monthly-sum'
|
||||
verbose_name = _("Monthly Sum")
|
||||
|
||||
def filter(self, dataset):
|
||||
today = timezone.now()
|
||||
return dataset.filter(
|
||||
created_at__year=today.year,
|
||||
created_at__month=today.month
|
||||
)
|
||||
|
||||
|
||||
class MonthlyAvg(MonthlySum):
|
||||
name = 'monthly-avg'
|
||||
verbose_name = _("Monthly AVG")
|
||||
|
||||
def get_epoch(self):
|
||||
today = timezone.now()
|
||||
return datetime(
|
||||
year=today.year,
|
||||
month=today.month,
|
||||
day=1,
|
||||
tzinfo=timezone.utc
|
||||
)
|
||||
|
||||
def compute_usage(self, dataset):
|
||||
last = dataset.latest()
|
||||
epoch = self.get_epoch()
|
||||
total = (last.created_at-epoch).total_seconds()
|
||||
ini = epoch
|
||||
result = 0
|
||||
for data in dataset:
|
||||
slot = (data.created_at-ini).total_seconds()
|
||||
result += data.value * slot/total
|
||||
ini = data.created_at
|
||||
return result
|
||||
|
||||
|
||||
class Last10DaysAvg(MonthlyAvg):
|
||||
name = 'last-10-days-avg'
|
||||
verbose_name = _("Last 10 days AVG")
|
||||
days = 10
|
||||
|
||||
def get_epoch(self):
|
||||
today = timezone.now()
|
||||
return today - datetime.timedelta(days=self.days)
|
||||
|
||||
def filter(self, dataset):
|
||||
return dataset.filter(created_at__gt=self.get_epoch())
|
|
@ -11,11 +11,12 @@ from djcelery.models import PeriodicTask, CrontabSchedule
|
|||
from orchestra.core import validators
|
||||
from orchestra.models import queryset, fields
|
||||
from orchestra.models.utils import get_model_field_path
|
||||
from orchestra.utils.paths import get_project_root
|
||||
from orchestra.utils.paths import get_project_dir
|
||||
from orchestra.utils.system import run
|
||||
|
||||
from . import helpers, tasks
|
||||
from . import tasks
|
||||
from .backends import ServiceMonitor
|
||||
from .methods import DataMethod
|
||||
from .validators import validate_scale
|
||||
|
||||
|
||||
|
@ -34,8 +35,8 @@ class Resource(models.Model):
|
|||
MONTHLY_AVG = 'MONTHLY_AVG'
|
||||
PERIODS = (
|
||||
(LAST, _("Last")),
|
||||
(MONTHLY_SUM, _("Monthly Sum")),
|
||||
(MONTHLY_AVG, _("Monthly Average")),
|
||||
(MONTHLY_SUM, _("Monthly sum")),
|
||||
(MONTHLY_AVG, _("Monthly avg")),
|
||||
)
|
||||
_related = set() # keeps track of related models for resource cleanup
|
||||
|
||||
|
@ -46,9 +47,10 @@ class Resource(models.Model):
|
|||
verbose_name = models.CharField(_("verbose name"), max_length=256)
|
||||
content_type = models.ForeignKey(ContentType,
|
||||
help_text=_("Model where this resource will be hooked."))
|
||||
period = models.CharField(_("period"), max_length=16, choices=PERIODS,
|
||||
default=LAST,
|
||||
help_text=_("Operation used for aggregating this resource monitored data."))
|
||||
# TODO rename to aggregation
|
||||
period = models.CharField(_("aggregation"), max_length=16,
|
||||
choices=DataMethod.get_choices(), default=DataMethod.get_choices()[0][0],
|
||||
help_text=_("Method used for aggregating this resource monitored data."))
|
||||
on_demand = models.BooleanField(_("on demand"), default=False,
|
||||
help_text=_("If enabled the resource will not be pre-allocated, "
|
||||
"but allocated under the application demand"))
|
||||
|
@ -69,7 +71,7 @@ class Resource(models.Model):
|
|||
help_text=_("Crontab for periodic execution. "
|
||||
"Leave it empty to disable periodic monitoring"))
|
||||
monitors = fields.MultiSelectField(_("monitors"), max_length=256, blank=True,
|
||||
choices=ServiceMonitor.get_plugin_choices(),
|
||||
choices=ServiceMonitor.get_choices(),
|
||||
help_text=_("Monitor backends used for monitoring this resource."))
|
||||
is_active = models.BooleanField(_("active"), default=True)
|
||||
|
||||
|
@ -84,6 +86,15 @@ class Resource(models.Model):
|
|||
def __unicode__(self):
|
||||
return "{}-{}".format(str(self.content_type), self.name)
|
||||
|
||||
@cached_property
|
||||
def method_class(self):
|
||||
return DataMethod.get(self.period)
|
||||
|
||||
@cached_property
|
||||
def method_instance(self):
|
||||
""" Per request lived type_instance """
|
||||
return self.method_class(self)
|
||||
|
||||
def clean(self):
|
||||
self.verbose_name = self.verbose_name.strip()
|
||||
if self.on_demand and self.default_allocation:
|
||||
|
@ -114,7 +125,7 @@ class Resource(models.Model):
|
|||
self.sync_periodic_task()
|
||||
# This only work on tests (multiprocessing used on real deployments)
|
||||
apps.get_app_config('resources').reload_relations()
|
||||
run('sleep 2 && touch %s/wsgi.py' % get_project_root(), async=True)
|
||||
run('sleep 2 && touch %s/wsgi.py' % get_project_dir(), async=True)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super(Resource, self).delete(*args, **kwargs)
|
||||
|
@ -201,7 +212,16 @@ class ResourceData(models.Model):
|
|||
return self.resource.unit
|
||||
|
||||
def get_used(self):
|
||||
return helpers.compute_resource_usage(self)
|
||||
resource = data.resource
|
||||
total = 0
|
||||
has_result = False
|
||||
today = datetime.date.today()
|
||||
for dataset in data.get_monitor_datasets():
|
||||
usage = data.method_instance.compute_usage(dataset)
|
||||
if usage is not None:
|
||||
has_result = True
|
||||
total += usage
|
||||
return float(total)/resource.get_scale() if has_result else None
|
||||
|
||||
def update(self, current=None):
|
||||
if current is None:
|
||||
|
@ -218,10 +238,9 @@ class ResourceData(models.Model):
|
|||
|
||||
def get_monitor_datasets(self):
|
||||
resource = self.resource
|
||||
today = timezone.now()
|
||||
datasets = []
|
||||
for monitor in resource.monitors:
|
||||
path = self.resource.get_model_path(monitor)
|
||||
path = resource.get_model_path(monitor)
|
||||
if path == []:
|
||||
dataset = MonitorData.objects.filter(
|
||||
monitor=monitor,
|
||||
|
@ -239,30 +258,16 @@ class ResourceData(models.Model):
|
|||
content_type=ct,
|
||||
object_id__in=pks
|
||||
)
|
||||
if resource.period in (resource.MONTHLY_AVG, resource.MONTHLY_SUM):
|
||||
datasets.append(
|
||||
dataset.filter(
|
||||
created_at__year=today.year,
|
||||
created_at__month=today.month
|
||||
)
|
||||
)
|
||||
elif resource.period == resource.LAST:
|
||||
# Get last monitoring data per object_id
|
||||
try:
|
||||
datasets.append(
|
||||
dataset.order_by('object_id', '-id').distinct('object_id')
|
||||
)
|
||||
except MonitorData.DoesNotExist:
|
||||
continue
|
||||
else:
|
||||
raise NotImplementedError("%s support not implemented" % self.period)
|
||||
datasets.append(
|
||||
resource.method_instance.filter(dataset)
|
||||
)
|
||||
return datasets
|
||||
|
||||
|
||||
class MonitorData(models.Model):
|
||||
""" Stores monitored data """
|
||||
monitor = models.CharField(_("monitor"), max_length=256,
|
||||
choices=ServiceMonitor.get_plugin_choices())
|
||||
choices=ServiceMonitor.get_choices())
|
||||
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
|
||||
object_id = models.PositiveIntegerField(_("object id"))
|
||||
created_at = models.DateTimeField(_("created"), default=timezone.now)
|
||||
|
|
52
orchestra/apps/saas/backends/bscw.py
Normal file
52
orchestra/apps/saas/backends/bscw.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.apps.orchestration import ServiceController
|
||||
|
||||
from .. import settings
|
||||
|
||||
|
||||
class BSCWBackend(ServiceController):
|
||||
verbose_name = _("BSCW SaaS")
|
||||
model = 'saas.SaaS'
|
||||
default_route_match = "saas.service == 'bscw'"
|
||||
actions = ('save', 'delete', 'validate_creation')
|
||||
|
||||
def validate_creation(self, saas):
|
||||
context = self.get_context(saas)
|
||||
self.append(textwrap.dedent("""\
|
||||
if [[ $(%(bsadmin)s register %(email)s) ]]; then
|
||||
echo 'ValidationError: email-exists'
|
||||
elif [[ $(%(bsadmin)s users -n %(username)s) ]]; then
|
||||
echo 'ValidationError: username-exists'
|
||||
fi""") % context
|
||||
)
|
||||
|
||||
def save(self, saas):
|
||||
context = self.get_context(saas)
|
||||
if hasattr(saas, 'password'):
|
||||
self.append(textwrap.dedent("""\
|
||||
if [[ ! $(%(bsadmin)s register %(email)s) && ! $(%(bsadmin)s users -n %(username)s) ]]; then
|
||||
# Change password
|
||||
%(bsadmin)s chpwd %(username)s '%(password)s'
|
||||
else
|
||||
# Create new user
|
||||
%(bsadmin)s register -r %(email)s %(username)s '%(password)s'
|
||||
fi
|
||||
""") % context
|
||||
)
|
||||
elif saas.active:
|
||||
self.append("%(bsadmin)s chpwd -u %(username)s" % context)
|
||||
else:
|
||||
self.append("%(bsadmin)s chpwd -l %(username)s" % context)
|
||||
|
||||
def delete(self, saas):
|
||||
context = self.get_context(saas)
|
||||
self.append("%(bsadmin)s rmuser -n %(username)s" % context)
|
||||
|
||||
def get_context(self, saas):
|
||||
return {
|
||||
'bsadmin': settings.SAAS_BSCW_BSADMIN_PATH,
|
||||
'email': saas.data.get('email'),
|
||||
'username': saas.name,
|
||||
'password': getattr(saas, 'password', None),
|
||||
}
|
|
@ -95,9 +95,9 @@ class GitLabSaaSBackend(ServiceController):
|
|||
users = json.loads(requests.get(users_url, headers=self.headers).content)
|
||||
for user in users:
|
||||
if user['username'] == username:
|
||||
print 'user-exists'
|
||||
print 'ValidationError: user-exists'
|
||||
if user['email'] == email:
|
||||
print 'email-exists'
|
||||
print 'ValidationError: email-exists'
|
||||
|
||||
def validate_creation(self, saas):
|
||||
self.append(self._validate_creation, saas)
|
||||
|
|
|
@ -14,7 +14,7 @@ from .services import SoftwareService
|
|||
|
||||
class SaaS(models.Model):
|
||||
service = models.CharField(_("service"), max_length=32,
|
||||
choices=SoftwareService.get_plugin_choices())
|
||||
choices=SoftwareService.get_choices())
|
||||
name = models.CharField(_("Name"), max_length=64,
|
||||
help_text=_("Required. 64 characters or fewer. Letters, digits and ./-/_ only."),
|
||||
validators=[validators.validate_username])
|
||||
|
@ -41,7 +41,7 @@ class SaaS(models.Model):
|
|||
|
||||
@cached_property
|
||||
def service_class(self):
|
||||
return SoftwareService.get_plugin(self.service)
|
||||
return SoftwareService.get(self.service)
|
||||
|
||||
@cached_property
|
||||
def service_instance(self):
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.conf import settings
|
||||
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', (
|
||||
'orchestra.apps.saas.services.moodle.MoodleService',
|
||||
|
@ -17,18 +19,22 @@ SAAS_WORDPRESS_ADMIN_PASSWORD = getattr(settings, 'SAAS_WORDPRESSMU_ADMIN_PASSWO
|
|||
'secret'
|
||||
)
|
||||
|
||||
|
||||
SAAS_WORDPRESS_BASE_URL = getattr(settings, 'SAAS_WORDPRESS_BASE_URL',
|
||||
'http://blogs.orchestra.lan/'
|
||||
'http://blogs.{}/'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_DOKUWIKI_TEMPLATE_PATH = getattr(settings, 'SAAS_DOKUWIKI_TEMPLATE_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/template.tar.gz')
|
||||
'/home/httpd/htdocs/wikifarm/template.tar.gz'
|
||||
)
|
||||
|
||||
|
||||
SAAS_DOKUWIKI_FARM_PATH = getattr(settings, 'WEBSITES_DOKUWIKI_FARM_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/farm'
|
||||
)
|
||||
|
||||
|
||||
SAAS_DRUPAL_SITES_PATH = getattr(settings, 'WEBSITES_DRUPAL_SITES_PATH',
|
||||
'/home/httpd/htdocs/drupal-mu/sites/%(site_name)s'
|
||||
)
|
||||
|
@ -38,34 +44,42 @@ SAAS_PHPLIST_DB_NAME = getattr(settings, 'SAAS_PHPLIST_DB_NAME',
|
|||
'phplist_mu'
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_BASE_DOMAIN = getattr(settings, 'SAAS_PHPLIST_BASE_DOMAIN',
|
||||
'lists.orchestra.lan'
|
||||
'lists.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_SEAFILE_DOMAIN = getattr(settings, 'SAAS_SEAFILE_DOMAIN',
|
||||
'seafile.orchestra.lan'
|
||||
'seafile.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_SEAFILE_DEFAULT_QUOTA = getattr(settings, 'SAAS_SEAFILE_DEFAULT_QUOTA',
|
||||
50
|
||||
)
|
||||
|
||||
|
||||
SAAS_BSCW_DOMAIN = getattr(settings, 'SAAS_BSCW_DOMAIN',
|
||||
'bscw.orchestra.lan'
|
||||
'bscw.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
||||
SAAS_BSCW_DEFAULT_QUOTA = getattr(settings, 'SAAS_BSCW_DEFAULT_QUOTA',
|
||||
50
|
||||
)
|
||||
|
||||
SAAS_BSCW_BSADMIN_PATH = getattr(settings, 'SAAS_BSCW_BSADMIN_PATH',
|
||||
'/home/httpd/bscw/bin/bsadmin',
|
||||
)
|
||||
|
||||
|
||||
SAAS_GITLAB_ROOT_PASSWORD = getattr(settings, 'SAAS_GITLAB_ROOT_PASSWORD',
|
||||
'secret'
|
||||
)
|
||||
|
||||
|
||||
SAAS_GITLAB_DOMAIN = getattr(settings, 'SAAS_GITLAB_DOMAIN',
|
||||
'gitlab.orchestra.lan'
|
||||
'gitlab.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
|||
)
|
||||
actions = [update_orders, clone]
|
||||
change_view_actions = actions + [view_help]
|
||||
change_form_template = 'admin/services/service/change_form.html'
|
||||
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
""" Improve performance of account field and filter by account """
|
||||
|
|
|
@ -36,8 +36,8 @@ class ServiceHandler(plugins.Plugin):
|
|||
return getattr(self.service, attr)
|
||||
|
||||
@classmethod
|
||||
def get_plugin_choices(cls):
|
||||
choices = super(ServiceHandler, cls).get_plugin_choices()
|
||||
def get_choices(cls):
|
||||
choices = super(ServiceHandler, cls).get_choices()
|
||||
return [('', _("Default"))] + choices
|
||||
|
||||
def validate_content_type(self, service):
|
||||
|
|
|
@ -7,12 +7,13 @@ from django.db.models import Q
|
|||
from django.db.models.loading import get_model
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.module_loading import autodiscover_modules
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import string_concat, ugettext_lazy as _
|
||||
|
||||
from orchestra.core import caches, validators
|
||||
from orchestra.core.translations import ModelTranslation
|
||||
from orchestra.core.validators import validate_name
|
||||
from orchestra.models import queryset
|
||||
from orchestra.utils.python import import_class
|
||||
|
||||
from . import settings
|
||||
from .handlers import ServiceHandler
|
||||
|
@ -20,6 +21,8 @@ from .handlers import ServiceHandler
|
|||
|
||||
autodiscover_modules('handlers')
|
||||
|
||||
rate_class = import_class(settings.SERVICES_RATE_CLASS)
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
NEVER = ''
|
||||
|
@ -61,7 +64,7 @@ class Service(models.Model):
|
|||
help_text=_("Handler used for processing this Service. A handler "
|
||||
"enables customized behaviour far beyond what options "
|
||||
"here allow to."),
|
||||
choices=ServiceHandler.get_plugin_choices())
|
||||
choices=ServiceHandler.get_choices())
|
||||
is_active = models.BooleanField(_("active"), default=True)
|
||||
ignore_superusers = models.BooleanField(_("ignore superusers"), default=True,
|
||||
help_text=_("Designates whether superuser orders are marked as ignored by default or not."))
|
||||
|
@ -128,13 +131,10 @@ class Service(models.Model):
|
|||
),
|
||||
default=BILLING_PERIOD)
|
||||
rate_algorithm = models.CharField(_("rate algorithm"), max_length=16,
|
||||
help_text=_("Algorithm used to interprete the rating table."),
|
||||
# TODO this should be dynamic, retrieved from rate (plans) app
|
||||
choices=(
|
||||
('STEP_PRICE', _("Step price")),
|
||||
('MATCH_PRICE', _("Match price")),
|
||||
),
|
||||
default='STEP_PRICE')
|
||||
help_text=string_concat(_("Algorithm used to interprete the rating table."), *[
|
||||
string_concat('<br> ', method.verbose_name, ': ', method.help_text)
|
||||
for name, method in rate_class.get_methods().iteritems()
|
||||
]), choices=rate_class.get_choices(), default=rate_class.get_choices()[0][0])
|
||||
on_cancel = models.CharField(_("on cancel"), max_length=16,
|
||||
help_text=_("Defines the cancellation behaviour of this service."),
|
||||
choices=(
|
||||
|
@ -171,7 +171,7 @@ class Service(models.Model):
|
|||
def handler(self):
|
||||
""" Accessor of this service handler instance """
|
||||
if self.handler_type:
|
||||
return ServiceHandler.get_plugin(self.handler_type)(self)
|
||||
return ServiceHandler.get(self.handler_type)(self)
|
||||
return ServiceHandler(self)
|
||||
|
||||
def clean(self):
|
||||
|
@ -231,8 +231,7 @@ class Service(models.Model):
|
|||
|
||||
@property
|
||||
def rate_method(self):
|
||||
rate_model = type(self).rates.related.model
|
||||
return rate_model.get_methods()[self.rate_algorithm]
|
||||
return rate_class.get_methods()[self.rate_algorithm]
|
||||
|
||||
def update_orders(self, commit=True):
|
||||
order_model = get_model(settings.SERVICES_ORDER_MODEL)
|
||||
|
|
|
@ -9,13 +9,27 @@ SERVICES_SERVICE_TAXES = getattr(settings, 'SERVICES_SERVICE_TAXES', (
|
|||
(21, "21%"),
|
||||
))
|
||||
|
||||
SERVICES_SERVICE_DEFAULT_TAX = getattr(settings, 'SERVICES_SERVICE_DEFAULT_TAX', 0)
|
||||
|
||||
SERVICES_SERVICE_DEFAULT_TAX = getattr(settings, 'SERVICES_SERVICE_DEFAULT_TAX',
|
||||
0
|
||||
)
|
||||
|
||||
|
||||
SERVICES_SERVICE_ANUAL_BILLING_MONTH = getattr(settings, 'SERVICES_SERVICE_ANUAL_BILLING_MONTH', 1)
|
||||
SERVICES_SERVICE_ANUAL_BILLING_MONTH = getattr(settings, 'SERVICES_SERVICE_ANUAL_BILLING_MONTH',
|
||||
1
|
||||
)
|
||||
|
||||
|
||||
SERVICES_ORDER_MODEL = getattr(settings, 'SERVICES_ORDER_MODEL', 'orders.Order')
|
||||
SERVICES_ORDER_MODEL = getattr(settings, 'SERVICES_ORDER_MODEL',
|
||||
'orders.Order'
|
||||
)
|
||||
|
||||
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = getattr(settings, 'SERVICES_DEFAULT_IGNORE_PERIOD', 'TEN_DAYS')
|
||||
SERVICES_RATE_CLASS = getattr(settings, 'SERVICES_RATE_CLASS',
|
||||
'orchestra.apps.plans.models.Rate'
|
||||
)
|
||||
|
||||
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = getattr(settings, 'SERVICES_DEFAULT_IGNORE_PERIOD',
|
||||
'TEN_DAYS'
|
||||
)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
{% extends "orchestra/admin/change_form.html" %}
|
||||
{% load i18n admin_urls admin_static admin_modify %}
|
||||
|
||||
|
||||
{% block object-tools %}
|
||||
{% if add %}
|
||||
<ul class="object-tools">
|
||||
<li><select name="forma" onchange="location = this.options[this.selectedIndex].value;" style="margin: -3px 0 0 0;">
|
||||
<option selected disabled>{% trans "Templates" %}</option>
|
||||
<option value="./?description=Mailbox&
|
||||
content_type=10&
|
||||
match=mailbox.active&
|
||||
handler_type=&
|
||||
is_active=True&
|
||||
ignore_superusers=True&
|
||||
billing_period=ANUAL&
|
||||
billing_point=ON_FIXED_DATE&
|
||||
is_fee=&
|
||||
order_description=&
|
||||
ignore_period=TEN_DAYS&
|
||||
metric=&
|
||||
nominal_price=28.10&
|
||||
tax=21&
|
||||
pricing_period=BILLING_PERIOD&
|
||||
rate_algorithm=MATCH_PRICE&
|
||||
on_cancel=COMPENSATE&
|
||||
payment_style=PREPAY">Mailbox</option>
|
||||
<option value="./?description=Database&
|
||||
content_type=19&
|
||||
match=database.account.is_active&handler_type=&
|
||||
is_active=True&
|
||||
ignore_superusers=True&
|
||||
billing_period=ANUAL&
|
||||
billing_point=ON_FIXED_DATE&
|
||||
is_fee=&
|
||||
order_description=&
|
||||
ignore_period=TEN_DAYS&
|
||||
metric=&
|
||||
nominal_price=24.79&
|
||||
tax=21&
|
||||
pricing_period=BILLING_PERIOD&
|
||||
rate_algorithm=STEP_PRICE&
|
||||
on_cancel=COMPENSATE&
|
||||
payment_style=PREPAY">Database</option>
|
||||
</select></li>
|
||||
<li>
|
||||
<a href="./help" class="historylink">{% trans "Help" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
|
@ -11,7 +11,9 @@ SYSTEMUSERS_SHELLS = getattr(settings, 'SYSTEMUSERS_SHELLS', (
|
|||
))
|
||||
|
||||
|
||||
SYSTEMUSERS_DEFAULT_SHELL = getattr(settings, 'SYSTEMUSERS_DEFAULT_SHELL', '/dev/null')
|
||||
SYSTEMUSERS_DEFAULT_SHELL = getattr(settings, 'SYSTEMUSERS_DEFAULT_SHELL',
|
||||
'/dev/null'
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_DISABLED_SHELLS = getattr(settings, 'SYSTEMUSERS_DISABLED_SHELLS', (
|
||||
|
@ -20,11 +22,16 @@ SYSTEMUSERS_DISABLED_SHELLS = getattr(settings, 'SYSTEMUSERS_DISABLED_SHELLS', (
|
|||
))
|
||||
|
||||
|
||||
SYSTEMUSERS_HOME = getattr(settings, 'SYSTEMUSERS_HOME', '/home/./%(user)s')
|
||||
SYSTEMUSERS_HOME = getattr(settings, 'SYSTEMUSERS_HOME',
|
||||
'/home/%(user)s'
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_FTP_LOG_PATH = getattr(settings, 'SYSTEMUSERS_FTP_LOG_PATH', '/var/log/vsftpd.log')
|
||||
SYSTEMUSERS_FTP_LOG_PATH = getattr(settings, 'SYSTEMUSERS_FTP_LOG_PATH',
|
||||
'/var/log/vsftpd.log'
|
||||
)
|
||||
|
||||
|
||||
SYSTEMUSERS_DEFAULT_GROUP_MEMBERS = getattr(settings, 'SYSTEMUSERS_DEFAULT_GROUP_MEMBERS',
|
||||
('www-data',))
|
||||
('www-data',)
|
||||
)
|
||||
|
|
|
@ -6,7 +6,9 @@ VPS_TYPES = getattr(settings, 'VPS_TYPES', (
|
|||
))
|
||||
|
||||
|
||||
VPS_DEFAULT_TYPE = getattr(settings, 'VPS_DEFAULT_TYPE', 'openvz')
|
||||
VPS_DEFAULT_TYPE = getattr(settings, 'VPS_DEFAULT_TYPE',
|
||||
'openvz'
|
||||
)
|
||||
|
||||
|
||||
VPS_TEMPLATES = getattr(settings, 'VPS_TEMPLATES', (
|
||||
|
@ -14,4 +16,6 @@ VPS_TEMPLATES = getattr(settings, 'VPS_TEMPLATES', (
|
|||
))
|
||||
|
||||
|
||||
VPS_DEFAULT_TEMPLATE = getattr(settings, 'VPS_DEFAULT_TEMPLATE', 'debian7')
|
||||
VPS_DEFAULT_TEMPLATE = getattr(settings, 'VPS_DEFAULT_TEMPLATE',
|
||||
'debian7'
|
||||
)
|
||||
|
|
|
@ -36,7 +36,7 @@ class WebAppOptionInline(admin.TabularInline):
|
|||
plugin = self.parent_object.type_class
|
||||
else:
|
||||
request = kwargs['request']
|
||||
plugin = AppType.get_plugin(request.GET['type'])
|
||||
plugin = AppType.get(request.GET['type'])
|
||||
kwargs['choices'] = plugin.get_options_choices()
|
||||
# Help text based on select widget
|
||||
target = 'this.id.replace("name", "value")'
|
||||
|
|
|
@ -22,7 +22,7 @@ class WebApp(models.Model):
|
|||
""" Represents a web application """
|
||||
name = models.CharField(_("name"), max_length=128, validators=[validators.validate_name])
|
||||
type = models.CharField(_("type"), max_length=32,
|
||||
choices=AppType.get_plugin_choices())
|
||||
choices=AppType.get_choices())
|
||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||
related_name='webapps')
|
||||
data = JSONField(_("data"), blank=True, default={},
|
||||
|
@ -45,7 +45,7 @@ class WebApp(models.Model):
|
|||
|
||||
@cached_property
|
||||
def type_class(self):
|
||||
return AppType.get_plugin(self.type)
|
||||
return AppType.get(self.type)
|
||||
|
||||
@cached_property
|
||||
def type_instance(self):
|
||||
|
@ -103,7 +103,7 @@ class WebAppOption(models.Model):
|
|||
|
||||
@cached_property
|
||||
def option_class(self):
|
||||
return AppOption.get_plugin(self.name)
|
||||
return AppOption.get(self.name)
|
||||
|
||||
@cached_property
|
||||
def option_instance(self):
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.settings import BASE_DOMAIN
|
||||
|
||||
|
||||
WEBAPPS_BASE_ROOT = getattr(settings, 'WEBAPPS_BASE_ROOT',
|
||||
'%(home)s/webapps/%(app_name)s/')
|
||||
'%(home)s/webapps/%(app_name)s/'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FPM_LISTEN = getattr(settings, 'WEBAPPS_FPM_LISTEN',
|
||||
|
@ -17,16 +20,19 @@ WEBAPPS_PHPFPM_POOL_PATH = getattr(settings, 'WEBAPPS_PHPFPM_POOL_PATH',
|
|||
|
||||
WEBAPPS_FCGID_WRAPPER_PATH = getattr(settings, 'WEBAPPS_FCGID_WRAPPER_PATH',
|
||||
# Inside SuExec Document root
|
||||
'/home/httpd/fcgi-bin.d/%(user)s/%(app_name)s-wrapper')
|
||||
'/home/httpd/fcgi-bin.d/%(user)s/%(app_name)s-wrapper'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_FCGID_CMD_OPTIONS_PATH = getattr(settings, 'WEBAPPS_FCGID_CMD_OPTIONS_PATH',
|
||||
# Loaded by Apache
|
||||
'/etc/apache2/fcgid-conf/%(user)s-%(app_name)s.conf')
|
||||
'/etc/apache2/fcgid-conf/%(user)s-%(app_name)s.conf'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_ERROR_LOG_PATH = getattr(settings, 'WEBAPPS_PHP_ERROR_LOG_PATH',
|
||||
'')
|
||||
''
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_MERGE_PHP_WEBAPPS = getattr(settings, 'WEBAPPS_MERGE_PHP_WEBAPPS',
|
||||
|
@ -55,29 +61,35 @@ WEBAPPS_PHP_VERSIONS = getattr(settings, 'WEBAPPS_PHP_VERSIONS', (
|
|||
|
||||
|
||||
WEBAPPS_DEFAULT_PHP_VERSION = getattr(settings, 'WEBAPPS_DEFAULT_PHP_VERSION',
|
||||
'5.4-cgi')
|
||||
'5.4-cgi'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_BINARY_PATH = getattr(settings, 'WEBAPPS_PHP_CGI_BINARY_PATH',
|
||||
# Path of the cgi binary used by fcgid
|
||||
'/usr/bin/php%(php_version_number)s-cgi')
|
||||
'/usr/bin/php%(php_version_number)s-cgi'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_RC_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_RC_DIR',
|
||||
# Path to php.ini
|
||||
'/etc/php%(php_version_number)s/cgi/')
|
||||
'/etc/php%(php_version_number)s/cgi/'
|
||||
)
|
||||
|
||||
|
||||
WEBAPPS_PHP_CGI_INI_SCAN_DIR = getattr(settings, 'WEBAPPS_PHP_CGI_INI_SCAN_DIR',
|
||||
# Path to php.ini
|
||||
'/etc/php%(php_version_number)s/cgi/conf.d')
|
||||
'/etc/php%(php_version_number)s/cgi/conf.d'
|
||||
)
|
||||
|
||||
|
||||
|
||||
WEBAPPS_UNDER_CONSTRUCTION_PATH = getattr(settings, 'WEBAPPS_UNDER_CONSTRUCTION_PATH',
|
||||
# Server-side path where a under construction stock page is
|
||||
# '/var/www/undercontruction/index.html',
|
||||
'')
|
||||
''
|
||||
)
|
||||
|
||||
|
||||
#WEBAPPS_TYPES_OVERRIDE = getattr(settings, 'WEBAPPS_TYPES_OVERRIDE', {})
|
||||
#for webapp_type, value in WEBAPPS_TYPES_OVERRIDE.iteritems():
|
||||
|
@ -151,4 +163,5 @@ WEBAPPS_ENABLED_OPTIONS = getattr(settings, 'WEBAPPS_ENABLED_OPTIONS', (
|
|||
|
||||
|
||||
WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = getattr(settings, 'WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',
|
||||
'mysql.orchestra.lan')
|
||||
'mysql.{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
|
|
@ -210,8 +210,8 @@ class Apache2Backend(ServiceController):
|
|||
location, target = proxy.split()
|
||||
location = normurlpath(source)
|
||||
proxy = textwrap.dedent("""\
|
||||
ProxyPass {location} {target}
|
||||
ProxyPassReverse {location} {target}""".format(
|
||||
ProxyPass {location}/ {target}
|
||||
ProxyPassReverse {location}/ {target}""".format(
|
||||
location=location, target=target)
|
||||
)
|
||||
proxies.append((location, proxy))
|
||||
|
|
|
@ -40,7 +40,7 @@ class SiteDirective(Plugin):
|
|||
return groups
|
||||
|
||||
@classmethod
|
||||
def get_plugin_choices(cls):
|
||||
def get_choices(cls):
|
||||
""" Generates grouped choices ready to use in Field.choices """
|
||||
# generators can not be @cached
|
||||
yield (None, '-------')
|
||||
|
|
|
@ -104,7 +104,7 @@ class WebsiteDirective(models.Model):
|
|||
website = models.ForeignKey(Website, verbose_name=_("web site"),
|
||||
related_name='directives')
|
||||
name = models.CharField(_("name"), max_length=128,
|
||||
choices=SiteDirective.get_plugin_choices())
|
||||
choices=SiteDirective.get_choices())
|
||||
value = models.CharField(_("value"), max_length=256)
|
||||
|
||||
def __unicode__(self):
|
||||
|
@ -112,7 +112,7 @@ class WebsiteDirective(models.Model):
|
|||
|
||||
@cached_property
|
||||
def directive_class(self):
|
||||
return SiteDirective.get_plugin(self.name)
|
||||
return SiteDirective.get(self.name)
|
||||
|
||||
@cached_property
|
||||
def directive_instance(self):
|
||||
|
|
|
@ -3,7 +3,8 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
|
||||
WEBSITES_UNIQUE_NAME_FORMAT = getattr(settings, 'WEBSITES_UNIQUE_NAME_FORMAT',
|
||||
'%(user)s-%(site_name)s')
|
||||
'%(user)s-%(site_name)s'
|
||||
)
|
||||
|
||||
|
||||
# TODO 'http', 'https', 'https-only', 'http and https' and rename to PROTOCOL
|
||||
|
@ -20,15 +21,19 @@ WEBSITES_PROTOCOL_CHOICES = getattr(settings, 'WEBSITES_PROTOCOL_CHOICES', (
|
|||
('https-only', _("HTTPS only")),
|
||||
))
|
||||
|
||||
WEBSITES_DEFAULT_PROTOCOL = getattr(settings, 'WEBSITES_DEFAULT_PROTOCOL', 'http')
|
||||
|
||||
#WEBSITES_DEFAULT_PORT = getattr(settings, 'WEBSITES_DEFAULT_PORT', 80)
|
||||
WEBSITES_DEFAULT_PROTOCOL = getattr(settings, 'WEBSITES_DEFAULT_PROTOCOL',
|
||||
'http'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_DEFAULT_IP = getattr(settings, 'WEBSITES_DEFAULT_IP', '*')
|
||||
WEBSITES_DEFAULT_IP = getattr(settings, 'WEBSITES_DEFAULT_IP',
|
||||
'*'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL', 'domains.Domain')
|
||||
WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL',
|
||||
'domains.Domain'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_ENABLED_DIRECTIVES = getattr(settings, 'WEBSITES_ENABLED_DIRECTIVES', (
|
||||
|
@ -47,23 +52,28 @@ WEBSITES_ENABLED_DIRECTIVES = getattr(settings, 'WEBSITES_ENABLED_DIRECTIVES', (
|
|||
|
||||
|
||||
WEBSITES_BASE_APACHE_CONF = getattr(settings, 'WEBSITES_BASE_APACHE_CONF',
|
||||
'/etc/apache2/')
|
||||
'/etc/apache2/'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_WEBALIZER_PATH = getattr(settings, 'WEBSITES_WEBALIZER_PATH',
|
||||
'/home/httpd/webalizer/')
|
||||
'/home/httpd/webalizer/'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH = getattr(settings, 'WEBSITES_WEBSITE_WWW_ACCESS_LOG_PATH',
|
||||
'/var/log/apache2/virtual/%(unique_name)s.log')
|
||||
'/var/log/apache2/virtual/%(unique_name)s.log'
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH = getattr(settings, 'WEBSITES_WEBSITE_WWW_ERROR_LOG_PATH',
|
||||
'')
|
||||
''
|
||||
)
|
||||
|
||||
|
||||
WEBSITES_TRAFFIC_IGNORE_HOSTS = getattr(settings, 'WEBSITES_TRAFFIC_IGNORE_HOSTS',
|
||||
('127.0.0.1',))
|
||||
('127.0.0.1',)
|
||||
)
|
||||
|
||||
|
||||
#WEBSITES_DEFAULT_SSl_CA = getattr(settings, 'WEBSITES_DEFAULT_SSl_CA',
|
||||
|
|
|
@ -3,34 +3,40 @@ import os
|
|||
from django.core.management.commands import makemessages
|
||||
|
||||
from orchestra.core.translations import ModelTranslation
|
||||
from orchestra.utils.paths import get_site_root
|
||||
from orchestra.utils.paths import get_site_dir
|
||||
|
||||
|
||||
class Command(makemessages.Command):
|
||||
""" Provides database translations support """
|
||||
|
||||
def handle(self, *args, **options):
|
||||
do_database = os.getcwd() == get_site_root()
|
||||
self.generated_database_files = []
|
||||
if do_database:
|
||||
self.project_locale_path = get_site_root()
|
||||
self.generate_database_files()
|
||||
super(Command, self).handle(*args, **options)
|
||||
self.remove_database_files()
|
||||
self.database_files = []
|
||||
try:
|
||||
if os.getcwd() == get_site_dir():
|
||||
self.generate_database_files()
|
||||
super(Command, self).handle(*args, **options)
|
||||
finally:
|
||||
self.remove_database_files()
|
||||
|
||||
def get_contents(self):
|
||||
for model, fields in ModelTranslation._registry.iteritems():
|
||||
contents = []
|
||||
for field in fields:
|
||||
contents = []
|
||||
for content in model.objects.values_list('id', field):
|
||||
pk, value = content
|
||||
contents.append(
|
||||
(pk, u"_(u'%s')" % value)
|
||||
)
|
||||
yield ('_'.join((model._meta.db_table, field)), contents)
|
||||
if contents:
|
||||
yield ('_'.join((model._meta.db_table, field)), contents)
|
||||
|
||||
def generate_database_files(self):
|
||||
""" tmp files are generated because of having a nice gettext location """
|
||||
"""
|
||||
Tmp files are generated because:
|
||||
1) having a nice gettext location
|
||||
# database_db_table_field.sql.py:id
|
||||
|
||||
2) Django's makemessages will work with no modifications
|
||||
"""
|
||||
for name, contents in self.get_contents():
|
||||
name = unicode(name)
|
||||
maximum = None
|
||||
|
@ -43,11 +49,11 @@ class Command(makemessages.Command):
|
|||
for ix in xrange(maximum+1):
|
||||
tmpcontent.append(content.get(ix, ''))
|
||||
tmpcontent = u'\n'.join(tmpcontent) + '\n'
|
||||
filepath = os.path.join(self.project_locale_path, 'database_%s.sql.py' % name)
|
||||
self.generated_database_files.append(filepath)
|
||||
with open(filepath, 'w') as tmpfile:
|
||||
filename = 'database_%s.sql.py' % name
|
||||
self.database_files.append(filename)
|
||||
with open(filename, 'w') as tmpfile:
|
||||
tmpfile.write(tmpcontent.encode('utf-8'))
|
||||
|
||||
def remove_database_files(self):
|
||||
for path in self.generated_database_files:
|
||||
for path in self.database_files:
|
||||
os.unlink(path)
|
||||
|
|
|
@ -4,7 +4,7 @@ from optparse import make_option
|
|||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from orchestra.utils.paths import get_site_root
|
||||
from orchestra.utils.paths import get_site_dir
|
||||
from orchestra.utils.system import run, check_root
|
||||
|
||||
|
||||
|
@ -66,7 +66,7 @@ class Command(BaseCommand):
|
|||
run('chmod +x %s' % orchestra_admin)
|
||||
run("%s install_requirements" % orchestra_admin)
|
||||
|
||||
manage_path = os.path.join(get_site_root(), 'manage.py')
|
||||
manage_path = os.path.join(get_site_dir(), 'manage.py')
|
||||
run("python %s collectstatic --noinput" % manage_path)
|
||||
run("python %s syncdb --noinput" % manage_path)
|
||||
run("python %s migrate --noinput" % manage_path)
|
||||
|
|
|
@ -4,7 +4,7 @@ from os import path
|
|||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from orchestra.utils.paths import get_site_root, get_orchestra_root
|
||||
from orchestra.utils.paths import get_site_dir, get_orchestra_dir
|
||||
from orchestra.utils.system import run, check_root
|
||||
|
||||
|
||||
|
@ -28,9 +28,9 @@ class Command(BaseCommand):
|
|||
@check_root
|
||||
def handle(self, *args, **options):
|
||||
context = {
|
||||
'site_root': get_site_root(),
|
||||
'site_dir': get_site_dir(),
|
||||
'username': options.get('username'),
|
||||
'bin_path': path.join(get_orchestra_root(), 'bin'),
|
||||
'bin_path': path.join(get_orchestra_dir(), 'bin'),
|
||||
'processes': options.get('processes'),
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ class Command(BaseCommand):
|
|||
CELERYD_NODES="w1"
|
||||
|
||||
# Where to chdir at start.
|
||||
CELERYD_CHDIR="%(site_root)s"
|
||||
CELERYD_CHDIR="%(site_dir)s"
|
||||
|
||||
# How to call "manage.py celeryd_multi"
|
||||
CELERYD_MULTI="$CELERYD_CHDIR/manage.py celeryd_multi"
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
|||
from django.core.management.base import BaseCommand
|
||||
from django.utils.six.moves import input
|
||||
|
||||
from orchestra.utils.paths import get_project_root, get_site_root, get_project_name
|
||||
from orchestra.utils.paths import get_project_dir, get_site_dir, get_project_name
|
||||
from orchestra.utils.system import run, check_root, get_default_celeryd_username
|
||||
|
||||
|
||||
|
@ -34,8 +34,8 @@ class Command(BaseCommand):
|
|||
|
||||
context = {
|
||||
'project_name': get_project_name(),
|
||||
'project_root': get_project_root(),
|
||||
'site_root': get_site_root(),
|
||||
'project_dir': get_project_dir(),
|
||||
'site_dir': get_site_dir(),
|
||||
'static_root': settings.STATIC_ROOT,
|
||||
'user': options.get('user'),
|
||||
'group': options.get('group') or options.get('user'),
|
||||
|
@ -61,7 +61,7 @@ class Command(BaseCommand):
|
|||
uwsgi_conf = (
|
||||
'[uwsgi]\n'
|
||||
'plugins = python\n'
|
||||
'chdir = %(site_root)s\n'
|
||||
'chdir = %(site_dir)s\n'
|
||||
'module = %(project_name)s.wsgi\n'
|
||||
'master = true\n'
|
||||
'processes = %(processes)d\n'
|
||||
|
|
|
@ -3,7 +3,7 @@ from optparse import make_option
|
|||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from orchestra.utils.paths import get_project_root
|
||||
from orchestra.utils.paths import get_project_dir
|
||||
from orchestra.utils.system import run, check_root
|
||||
|
||||
|
||||
|
@ -42,7 +42,7 @@ class Command(BaseCommand):
|
|||
|
||||
run('su postgres -c "psql -c \\"CREATE USER %(db_user)s PASSWORD \'%(db_password)s\';\\""' % context, error_codes=[0,1])
|
||||
run('su postgres -c "psql -c \\"CREATE DATABASE %(db_name)s OWNER %(db_user)s;\\""' % context, error_codes=[0,1])
|
||||
context.update({'settings': os.path.join(get_project_root(), 'settings.py')})
|
||||
context.update({'settings': os.path.join(get_project_dir(), 'settings.py')})
|
||||
|
||||
if run("grep 'DATABASES' %(settings)s" % context, error_codes=[0,1]).return_code == 0:
|
||||
# Update existing settings_file
|
||||
|
|
|
@ -7,7 +7,7 @@ import sys
|
|||
from django.core.management.base import BaseCommand
|
||||
from pyflakes import checker, messages
|
||||
|
||||
from orchestra.utils.paths import get_orchestra_root
|
||||
from orchestra.utils.paths import get_orchestra_dir
|
||||
|
||||
|
||||
# BlackHole, PySyntaxError and checking based on
|
||||
|
@ -93,7 +93,7 @@ class Command(BaseCommand):
|
|||
|
||||
def handle(self, *filenames, **options):
|
||||
if not filenames:
|
||||
filenames = [get_orchestra_root(), '.']
|
||||
filenames = [get_orchestra_dir(), '.']
|
||||
warnings = checkPaths(filenames)
|
||||
for warning in warnings:
|
||||
print warning
|
||||
|
|
|
@ -17,7 +17,7 @@ class SelectPluginAdminMixin(object):
|
|||
plugin = getattr(obj, '%s_instance' % self.plugin_field)
|
||||
self.form = getattr(plugin, 'get_change_form', plugin.get_form)()
|
||||
else:
|
||||
plugin = self.plugin.get_plugin(self.plugin_value)()
|
||||
plugin = self.plugin.get(self.plugin_value)()
|
||||
self.form = plugin.get_form()
|
||||
return super(SelectPluginAdminMixin, self).get_form(request, obj, **kwargs)
|
||||
|
||||
|
@ -67,7 +67,7 @@ class SelectPluginAdminMixin(object):
|
|||
self.plugin_value = plugin_value
|
||||
if not plugin_value:
|
||||
self.plugin_value = self.plugin.get_plugins()[0].get_name()
|
||||
plugin = self.plugin.get_plugin(self.plugin_value)
|
||||
plugin = self.plugin.get(self.plugin_value)
|
||||
context = {
|
||||
'title': _("Add new %s") % plugin.verbose_name,
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class Plugin(object):
|
|||
return cls.plugins
|
||||
|
||||
@classmethod
|
||||
def get_plugin(cls, name):
|
||||
def get(cls, name):
|
||||
if not hasattr(cls, '_registry'):
|
||||
cls._registry = {
|
||||
plugin.get_name(): plugin for plugin in cls.get_plugins()
|
||||
|
@ -44,7 +44,7 @@ class Plugin(object):
|
|||
return cls.get_name()
|
||||
|
||||
@classmethod
|
||||
def get_plugin_choices(cls):
|
||||
def get_choices(cls):
|
||||
choices = []
|
||||
for plugin in cls.get_plugins():
|
||||
verbose = plugin.get_verbose_name()
|
||||
|
@ -102,7 +102,7 @@ class PluginModelAdapter(Plugin):
|
|||
return plugins
|
||||
|
||||
@classmethod
|
||||
def get_plugin(cls, name):
|
||||
def get(cls, name):
|
||||
# don't cache, since models can change
|
||||
for plugin in cls.get_plugins():
|
||||
if name == plugin.get_name():
|
||||
|
|
|
@ -4,32 +4,55 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
# Domain name used when it will not be possible to infere the domain from a request
|
||||
# For example in periodic tasks
|
||||
SITE_URL = getattr(settings, 'SITE_URL', 'http://localhost')
|
||||
SITE_URL = getattr(settings, 'SITE_URL',
|
||||
'http://localhost'
|
||||
)
|
||||
|
||||
SITE_NAME = getattr(settings, 'SITE_NAME',
|
||||
'orchestra'
|
||||
)
|
||||
|
||||
SITE_NAME = getattr(settings, 'SITE_NAME', 'confine')
|
||||
|
||||
SITE_VERBOSE_NAME = getattr(settings, 'SITE_VERBOSE_NAME',
|
||||
_("%s Hosting Management" % SITE_NAME.capitalize()))
|
||||
_("%s Hosting Management" % SITE_NAME.capitalize())
|
||||
)
|
||||
|
||||
|
||||
BASE_DOMAIN = getattr(settings, 'BASE_DOMAIN',
|
||||
'orchestra.lan'
|
||||
)
|
||||
|
||||
# Service management commands
|
||||
|
||||
START_SERVICES = getattr(settings, 'START_SERVICES',
|
||||
['postgresql', 'celeryevcam', 'celeryd', 'celerybeat', ('uwsgi', 'nginx'),]
|
||||
START_SERVICES = getattr(settings, 'START_SERVICES', [
|
||||
'postgresql',
|
||||
'celeryevcam',
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
('uwsgi', 'nginx'),
|
||||
])
|
||||
|
||||
|
||||
RESTART_SERVICES = getattr(settings, 'RESTART_SERVICES', [
|
||||
'celeryd',
|
||||
'celerybeat',
|
||||
'uwsgi'
|
||||
])
|
||||
|
||||
STOP_SERVICES = getattr(settings, 'STOP_SERVICES', [
|
||||
('uwsgi', 'nginx'),
|
||||
'celerybeat',
|
||||
'celeryd',
|
||||
'celeryevcam',
|
||||
'postgresql'
|
||||
])
|
||||
|
||||
|
||||
API_ROOT_VIEW = getattr(settings, 'API_ROOT_VIEW',
|
||||
'orchestra.api.root.APIRoot'
|
||||
)
|
||||
|
||||
RESTART_SERVICES = getattr(settings, 'RESTART_SERVICES',
|
||||
['celeryd', 'celerybeat', 'uwsgi']
|
||||
)
|
||||
|
||||
STOP_SERVICES = getattr(settings, 'STOP_SERVICES',
|
||||
[('uwsgi', 'nginx'), 'celerybeat', 'celeryd', 'celeryevcam', 'postgresql']
|
||||
)
|
||||
|
||||
|
||||
API_ROOT_VIEW = getattr(settings, 'API_ROOT_VIEW', 'orchestra.api.root.APIRoot')
|
||||
|
||||
|
||||
ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = getattr(settings, 'ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL',
|
||||
'support@orchestra.lan'
|
||||
'support@{}'.format(BASE_DOMAIN)
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
|
||||
|
||||
def get_project_root():
|
||||
def get_project_dir():
|
||||
""" Return the current project path site/project """
|
||||
from django.conf import settings
|
||||
settings_file = os.sys.modules[settings.SETTINGS_MODULE].__file__
|
||||
|
@ -10,15 +10,15 @@ def get_project_root():
|
|||
|
||||
def get_project_name():
|
||||
""" Returns current project name """
|
||||
return os.path.basename(get_project_root())
|
||||
return os.path.basename(get_project_dir())
|
||||
|
||||
|
||||
def get_site_root():
|
||||
def get_site_dir():
|
||||
""" Returns project site path """
|
||||
return os.path.abspath(os.path.join(get_project_root(), '..'))
|
||||
return os.path.abspath(os.path.join(get_project_dir(), '..'))
|
||||
|
||||
|
||||
def get_orchestra_root():
|
||||
def get_orchestra_dir():
|
||||
""" Returns orchestra base path """
|
||||
import orchestra
|
||||
return os.path.dirname(os.path.realpath(orchestra.__file__))
|
||||
|
|
Loading…
Reference in a new issue