Random fixes
This commit is contained in:
parent
d5cc1b7d7c
commit
786b9a7657
5
TODO.md
5
TODO.md
|
@ -160,9 +160,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
|
||||
* service.name / verbose_name instead of .description ?
|
||||
* miscellaneous.name / verbose_name
|
||||
* service.invoice_name
|
||||
|
||||
* Bills can have sublines?
|
||||
|
||||
* proforma without billing contact?
|
||||
|
||||
|
@ -177,3 +174,5 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
|||
* ManyToManyField.symmetrical = False (user group)
|
||||
|
||||
* REST PERMISSIONS
|
||||
|
||||
* caching based on def text2int(textnum, numwords={}):
|
||||
|
|
|
@ -42,7 +42,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
|||
)
|
||||
fieldsets = (
|
||||
(_("User"), {
|
||||
'fields': ('username', 'password',)
|
||||
'fields': ('username', 'password', 'main_systemuser_link')
|
||||
}),
|
||||
(_("Personal info"), {
|
||||
'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'),
|
||||
|
@ -59,12 +59,14 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
|||
add_form = AccountCreationForm
|
||||
form = UserChangeForm
|
||||
filter_horizontal = ()
|
||||
change_readonly_fields = ('username',)
|
||||
change_readonly_fields = ('username', 'main_systemuser_link')
|
||||
change_form_template = 'admin/accounts/account/change_form.html'
|
||||
actions = [disable]
|
||||
change_view_actions = actions
|
||||
list_select_related = ('billcontact',)
|
||||
|
||||
main_systemuser_link = admin_link('main_systemuser')
|
||||
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
""" Make value input widget bigger """
|
||||
if db_field.name == 'comments':
|
||||
|
@ -101,9 +103,11 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
|||
return fieldsets
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
super(AccountAdmin, self).save_model(request, obj, form, change)
|
||||
if not change:
|
||||
form.save_model(obj)
|
||||
form.save_related(obj)
|
||||
else:
|
||||
super(AccountAdmin, self).save_model(request, obj, form, change)
|
||||
|
||||
|
||||
admin.site.register(Account, AccountAdmin)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from django import forms
|
||||
from django.db.models.loading import get_model
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -9,12 +11,12 @@ from .models import Account
|
|||
|
||||
|
||||
def create_account_creation_form():
|
||||
fields = {
|
||||
'create_systemuser': forms.BooleanField(initial=True, required=False,
|
||||
label=_("Create systemuser"), widget=forms.CheckboxInput(attrs={'disabled': True}),
|
||||
help_text=_("Designates whether to creates a related system user with the same "
|
||||
"username and password or not."))
|
||||
}
|
||||
fields = OrderedDict(**{
|
||||
'enable_systemuser': forms.BooleanField(initial=True, required=False,
|
||||
label=_("Enable systemuser"),
|
||||
help_text=_("Designates whether to creates an enabled or disabled related system user. "
|
||||
"Notice that a related system user will be always created."))
|
||||
})
|
||||
for model, key, kwargs, help_text in settings.ACCOUNTS_CREATE_RELATED:
|
||||
model = get_model(model)
|
||||
field_name = 'create_%s' % model._meta.model_name
|
||||
|
@ -46,6 +48,8 @@ def create_account_creation_form():
|
|||
raise forms.ValidationError(
|
||||
_("A %s with this name already exists") % verbose_name
|
||||
)
|
||||
def save_model(self, account):
|
||||
account.save(active_systemuser=self.cleaned_data['enable_systemuser'])
|
||||
|
||||
def save_related(self, account):
|
||||
for model, key, related_kwargs, __ in settings.ACCOUNTS_CREATE_RELATED:
|
||||
|
@ -60,6 +64,7 @@ def create_account_creation_form():
|
|||
fields.update({
|
||||
'create_related_fields': fields.keys(),
|
||||
'clean': clean,
|
||||
'save_model': save_model,
|
||||
'save_related': save_related,
|
||||
})
|
||||
|
||||
|
|
|
@ -60,12 +60,12 @@ class Account(auth.AbstractBaseUser):
|
|||
def get_main(cls):
|
||||
return cls.objects.get(pk=settings.ACCOUNTS_MAIN_PK)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
def save(self, active_systemuser=False, *args, **kwargs):
|
||||
created = not self.pk
|
||||
super(Account, self).save(*args, **kwargs)
|
||||
if created:
|
||||
self.main_systemuser = self.systemusers.create(account=self, username=self.username,
|
||||
password=self.password)
|
||||
password=self.password, is_active=active_systemuser)
|
||||
self.save(update_fields=['main_systemuser'])
|
||||
|
||||
def clean(self):
|
||||
|
|
|
@ -146,10 +146,7 @@ class Order(models.Model):
|
|||
if account_id is None:
|
||||
# New account workaround -> user.account_id == None
|
||||
continue
|
||||
ignore = False
|
||||
account = getattr(instance, 'account', instance)
|
||||
if account.is_superuser:
|
||||
ignore = service.ignore_superusers
|
||||
ignore = service.handler.get_ignore(instance)
|
||||
order = cls(content_object=instance, service=service,
|
||||
account_id=account_id, ignore=ignore)
|
||||
if commit:
|
||||
|
@ -163,8 +160,7 @@ class Order(models.Model):
|
|||
order.update()
|
||||
elif orders:
|
||||
order = orders.get()
|
||||
if commit:
|
||||
order.cancel()
|
||||
order.cancel(commit=commit)
|
||||
updates.append((order, 'cancelled'))
|
||||
return updates
|
||||
|
||||
|
@ -188,9 +184,11 @@ class Order(models.Model):
|
|||
self.description = description
|
||||
self.save(update_fields=['description'])
|
||||
|
||||
def cancel(self):
|
||||
def cancel(self, commit=True):
|
||||
self.cancelled_on = timezone.now()
|
||||
self.save(update_fields=['cancelled_on'])
|
||||
self.ignore = self.service.handler.get_order_ignore(self)
|
||||
if commit:
|
||||
self.save(update_fields=['cancelled_on', 'ignore'])
|
||||
logger.info("CANCELLED order id: {id}".format(id=self.id))
|
||||
|
||||
def mark_as_ignored(self):
|
||||
|
|
|
@ -10,3 +10,23 @@ SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', (
|
|||
'orchestra.apps.saas.services.gitlab.GitLabService',
|
||||
'orchestra.apps.saas.services.phplist.PHPListService',
|
||||
))
|
||||
|
||||
|
||||
SAAS_WORDPRESSMU_BASE_URL = getattr(settings, 'SAAS_WORDPRESSMU_BASE_URL',
|
||||
'http://%(site_name)s.example.com')
|
||||
|
||||
|
||||
SAAS_WORDPRESSMU_ADMIN_PASSWORD = getattr(settings, 'SAAS_WORDPRESSMU_ADMIN_PASSWORD',
|
||||
'secret')
|
||||
|
||||
|
||||
SAAS_DOKUWIKIMU_TEMPLATE_PATH = setattr(settings, 'SAAS_DOKUWIKIMU_TEMPLATE_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/template.tar.gz')
|
||||
|
||||
|
||||
SAAS_DOKUWIKIMU_FARM_PATH = getattr(settings, 'SAAS_DOKUWIKIMU_FARM_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/farm')
|
||||
|
||||
|
||||
SAAS_DRUPAL_SITES_PATH = getattr(settings, 'SAAS_DRUPAL_SITES_PATH',
|
||||
'/home/httpd/htdocs/drupal-mu/sites/%(site_name)s')
|
||||
|
|
|
@ -42,7 +42,8 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
|||
}),
|
||||
(_("Billing options"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('billing_period', 'billing_point', 'is_fee', 'order_description')
|
||||
'fields': ('billing_period', 'billing_point', 'is_fee', 'order_description',
|
||||
'ignore_period')
|
||||
}),
|
||||
(_("Pricing options"), {
|
||||
'classes': ('wide',),
|
||||
|
|
|
@ -55,6 +55,32 @@ class ServiceHandler(plugins.Plugin):
|
|||
}
|
||||
return eval(self.match, safe_locals)
|
||||
|
||||
def get_ignore_delta(self):
|
||||
if self.ignore_period == self.NEVER:
|
||||
return None
|
||||
value, unit = self.ignore_period.split('_')
|
||||
value = text2int(value)
|
||||
if unit.lowe().startswith('day'):
|
||||
return timedelta(days=value)
|
||||
if unit.lowe().startswith('month'):
|
||||
return timedelta(months=value)
|
||||
else:
|
||||
raise ValueError("Unknown unit %s" % unit)
|
||||
|
||||
def get_order_ignore(self, order):
|
||||
""" service trial delta """
|
||||
ignore_delta = self.get_ignore_delta()
|
||||
if ignore_delta and (order.cancelled_on-ignore_delta).date() <= order.registered_on:
|
||||
return True
|
||||
return order.ignore
|
||||
|
||||
def get_ignore(self, instance):
|
||||
ignore = False
|
||||
account = getattr(instance, 'account', instance)
|
||||
if account.is_superuser:
|
||||
ignore = self.ignore_superusers
|
||||
return ignore
|
||||
|
||||
def get_metric(self, instance):
|
||||
if self.metric:
|
||||
safe_locals = {
|
||||
|
|
|
@ -89,6 +89,8 @@ class Service(models.Model):
|
|||
# DAILY = 'DAILY'
|
||||
MONTHLY = 'MONTHLY'
|
||||
ANUAL = 'ANUAL'
|
||||
ONE_DAY = 'ONE_DAY'
|
||||
TWO_DAYS = 'TWO_DAYS'
|
||||
TEN_DAYS = 'TEN_DAYS'
|
||||
ONE_MONTH = 'ONE_MONTH'
|
||||
ALWAYS = 'ALWAYS'
|
||||
|
@ -158,6 +160,17 @@ class Service(models.Model):
|
|||
"used for generating the description for the bill lines of this services.<br>"
|
||||
"Defaults to <tt>'%s: %s' % (handler.description, instance)</tt>"
|
||||
))
|
||||
ignore_period = models.CharField(_("ignore period"), max_length=16, blank=True,
|
||||
help_text=_("Period in which orders will be ignored if cancelled. "
|
||||
"Useful for designating <i>trial periods</i>"),
|
||||
choices=(
|
||||
(NEVER, _("No ignore")),
|
||||
(ONE_DAY, _("One day")),
|
||||
(TWO_DAYS, _("Two days")),
|
||||
(TEN_DAYS, _("Ten days")),
|
||||
(ONE_MONTH, _("One month")),
|
||||
),
|
||||
default=settings.SERVICES_DEFAULT_IGNORE_PERIOD)
|
||||
# Pricing
|
||||
metric = models.CharField(_("metric"), max_length=256, blank=True,
|
||||
help_text=_(
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from datetime import timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
@ -14,3 +16,6 @@ SERVICES_SERVICE_ANUAL_BILLING_MONTH = getattr(settings, 'SERVICES_SERVICE_ANUAL
|
|||
|
||||
|
||||
SERVICES_ORDER_MODEL = getattr(settings, 'SERVICES_ORDER_MODEL', 'orders.Order')
|
||||
|
||||
|
||||
SERVICES_DEFAULT_IGNORE_PERIOD = getattr(settings, 'SERVICES_DEFAULT_IGNORE_PERIOD', 'TWO_DAYS')
|
||||
|
|
|
@ -55,7 +55,6 @@ class WebAppServiceMixin(object):
|
|||
}
|
||||
|
||||
|
||||
|
||||
for __, module_name, __ in pkgutil.walk_packages(__path__):
|
||||
# sorry for the exec(), but Import module function fails :(
|
||||
exec('from . import %s' % module_name)
|
||||
|
|
|
@ -5,23 +5,17 @@ from django.db import models
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core import validators, services
|
||||
from orchestra.utils import tuple_setting_to_choices, dict_setting_to_choices
|
||||
from orchestra.utils.functional import cached
|
||||
|
||||
from . import settings
|
||||
|
||||
|
||||
def settings_to_choices(choices):
|
||||
return sorted(
|
||||
[ (name, opt[0]) for name,opt in choices.iteritems() ],
|
||||
key=lambda e: e[0]
|
||||
)
|
||||
|
||||
|
||||
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=settings_to_choices(settings.WEBAPPS_TYPES),
|
||||
choices=dict_setting_to_choices(settings.WEBAPPS_TYPES),
|
||||
default=settings.WEBAPPS_DEFAULT_TYPE)
|
||||
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
|
||||
related_name='webapps')
|
||||
|
@ -41,20 +35,23 @@ class WebApp(models.Model):
|
|||
def get_fpm_port(self):
|
||||
return settings.WEBAPPS_FPM_START_PORT + self.account.pk
|
||||
|
||||
def get_method(self):
|
||||
method = settings.WEBAPPS_TYPES[self.type]
|
||||
args = method[2] if len(method) == 4 else ()
|
||||
return method[1], args
|
||||
def get_directive(self):
|
||||
directive = settings.WEBAPPS_TYPES[self.type]['directive']
|
||||
args = directive[1:] if len(directive) > 1 else ()
|
||||
return directive[0], args
|
||||
|
||||
def get_path(self):
|
||||
context = {
|
||||
'user': self.account.username,
|
||||
'home': webapp.get_user().get_home(),
|
||||
'app_name': self.name,
|
||||
}
|
||||
return settings.WEBAPPS_BASE_ROOT % context
|
||||
|
||||
def get_user(self):
|
||||
return self.account.main_systemuser
|
||||
|
||||
def get_username(self):
|
||||
return self.account.username
|
||||
return self.get_user().username
|
||||
|
||||
def get_groupname(self):
|
||||
return self.get_username()
|
||||
|
@ -64,7 +61,7 @@ class WebAppOption(models.Model):
|
|||
webapp = models.ForeignKey(WebApp, verbose_name=_("Web application"),
|
||||
related_name='options')
|
||||
name = models.CharField(_("name"), max_length=128,
|
||||
choices=settings_to_choices(settings.WEBAPPS_OPTIONS))
|
||||
choices=tuple_setting_to_choices(settings.WEBAPPS_OPTIONS))
|
||||
value = models.CharField(_("value"), max_length=256)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -2,8 +2,7 @@ from django.conf import settings
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
# TODO make '%(mainuser_home)s/webapps...
|
||||
WEBAPPS_BASE_ROOT = getattr(settings, 'WEBAPPS_BASE_ROOT', '/home/%(user)s/webapps/%(app_name)s/')
|
||||
WEBAPPS_BASE_ROOT = getattr(settings, 'WEBAPPS_BASE_ROOT', '%(home)s/webapps/%(app_name)s/')
|
||||
|
||||
|
||||
WEBAPPS_FPM_LISTEN = getattr(settings, 'WEBAPPS_FPM_LISTEN',
|
||||
|
@ -23,57 +22,36 @@ WEBAPPS_FCGID_PATH = getattr(settings, 'WEBAPPS_FCGID_PATH',
|
|||
|
||||
|
||||
WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', {
|
||||
# { name: ( verbose_name, method_name, method_args, description) }
|
||||
'php5.5': (
|
||||
_("PHP 5.5"),
|
||||
'php5.5': {
|
||||
'verbose_name': "PHP 5.5",
|
||||
# 'fpm', ('unix:/var/run/%(user)s-%(app_name)s.sock|fcgi://127.0.0.1%(app_path)s',),
|
||||
'fpm', ('fcgi://127.0.0.1:%(fpm_port)s%(app_path)s',),
|
||||
_("This creates a PHP5.5 application under ~/webapps/<app_name>\n"
|
||||
'directive': ('fpm', 'fcgi://{}%(app_path)s'.format(WEBAPPS_FPM_LISTEN)),
|
||||
'help_text': _("This creates a PHP5.5 application under ~/webapps/<app_name><br>"
|
||||
"PHP-FPM will be used to execute PHP files.")
|
||||
),
|
||||
'php5.2': (
|
||||
_("PHP 5.2"),
|
||||
'fcgid', (WEBAPPS_FCGID_PATH,),
|
||||
_("This creates a PHP5.2 application under ~/webapps/<app_name>\n"
|
||||
},
|
||||
'php5.2': {
|
||||
'verbose_name': "PHP 5.2",
|
||||
'directive': ('fcgi', WEBAPPS_FCGID_PATH),
|
||||
'help_text': _("This creates a PHP5.2 application under ~/webapps/<app_name><br>"
|
||||
"Apache-mod-fcgid will be used to execute PHP files.")
|
||||
),
|
||||
'php4': (
|
||||
_("PHP 4"),
|
||||
'fcgid', (WEBAPPS_FCGID_PATH,),
|
||||
_("This creates a PHP4 application under ~/webapps/<app_name>\n"
|
||||
},
|
||||
'php4': {
|
||||
'verbose_name': "PHP 4",
|
||||
'directive': ('fcgi', WEBAPPS_FCGID_PATH,),
|
||||
'help_text': _("This creates a PHP4 application under ~/webapps/<app_name><br>"
|
||||
"Apache-mod-fcgid will be used to execute PHP files.")
|
||||
),
|
||||
'static': (
|
||||
_("Static"),
|
||||
'alias', (),
|
||||
_("This creates a Static application under ~/webapps/<app_name>\n"
|
||||
},
|
||||
'static': {
|
||||
'verbose_name': _("Static"),
|
||||
'directive': ('static',),
|
||||
'help_text': _("This creates a Static application under ~/webapps/<app_name><br>"
|
||||
"Apache2 will be used to serve static content and execute CGI files.")
|
||||
),
|
||||
# 'wordpress': (
|
||||
# _("Wordpress"),
|
||||
# 'fpm', ('fcgi://127.0.0.1:8990/home/httpd/wordpress-mu/',),
|
||||
# _("This creates a Wordpress site into a shared Wordpress server\n"
|
||||
# "By default this blog will be accessible via http://<app_name>.blogs.example.com")
|
||||
#
|
||||
# ),
|
||||
# 'dokuwiki': (
|
||||
# _("DokuWiki"),
|
||||
# 'alias', ('/home/httpd/wikifarm/farm/',),
|
||||
# _("This create a Dokuwiki wiki into a shared Dokuwiki server\n")
|
||||
# ),
|
||||
# 'drupal': (
|
||||
# _("Drupdal"),
|
||||
# 'fpm', ('fcgi://127.0.0.1:8991/home/httpd/drupal-mu/',),
|
||||
# _("This creates a Drupal site into a shared Drupal server\n"
|
||||
# "The installation will be completed after visiting "
|
||||
# "http://<app_name>.drupal.example.com/install.php?profile=standard&locale=ca\n"
|
||||
# "By default this site will be accessible via http://<app_name>.drupal.example.com")
|
||||
# ),
|
||||
'webalizer': (
|
||||
_("Webalizer"),
|
||||
'alias', ('%(app_path)s%(site_name)s',),
|
||||
_("This creates a Webalizer application under ~/webapps/<app_name>-<site_name>\n")
|
||||
),
|
||||
},
|
||||
'webalizer': {
|
||||
'verbose_name': "Webalizer",
|
||||
'directive': ('static', '%(app_path)s%(site_name)s'),
|
||||
'help_text': _("This creates a Webalizer application under ~/webapps/<app_name>-<site_name>")
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
|
@ -194,25 +172,3 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTIO
|
|||
'escapeshellarg',
|
||||
'dl'
|
||||
])
|
||||
|
||||
|
||||
# TODO
|
||||
WEBAPPS_WORDPRESSMU_BASE_URL = getattr(settings, 'WEBAPPS_WORDPRESSMU_BASE_URL',
|
||||
'http://blogs.example.com')
|
||||
|
||||
|
||||
WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD = getattr(settings, 'WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD',
|
||||
'secret')
|
||||
|
||||
|
||||
WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH = setattr(settings, 'WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/template.tar.gz')
|
||||
|
||||
|
||||
WEBAPPS_DOKUWIKIMU_FARM_PATH = getattr(settings, 'WEBAPPS_DOKUWIKIMU_FARM_PATH',
|
||||
'/home/httpd/htdocs/wikifarm/farm')
|
||||
|
||||
|
||||
WEBAPPS_DRUPAL_SITES_PATH = getattr(settings, 'WEBAPPS_DRUPAL_SITES_PATH',
|
||||
'/home/httpd/htdocs/drupal-mu/sites/%(app_name)s')
|
||||
|
||||
|
|
|
@ -65,12 +65,12 @@ class Apache2Backend(ServiceController):
|
|||
def get_content_directives(self, site):
|
||||
directives = ''
|
||||
for content in site.content_set.all().order_by('-path'):
|
||||
method, args = content.webapp.get_method()
|
||||
method, args = content.webapp.get_directive()
|
||||
method = getattr(self, 'get_%s_directives' % method)
|
||||
directives += method(content, *args)
|
||||
return directives
|
||||
|
||||
def get_alias_directives(self, content, *args):
|
||||
def get_static_directives(self, content, *args):
|
||||
context = self.get_content_context(content)
|
||||
context['path'] = args[0] % context if args else content.webapp.get_path()
|
||||
return "Alias %(location)s %(path)s\n" % context
|
||||
|
@ -81,10 +81,10 @@ class Apache2Backend(ServiceController):
|
|||
directive = "ProxyPassMatch ^%(location)s(.*\.php(/.*)?)$ %(fcgi_path)s$1\n"
|
||||
return directive % context
|
||||
|
||||
def get_fcgid_directives(self, content, fcgid_path):
|
||||
def get_fcgi_directives(self, content, fcgid_path):
|
||||
context = self.get_content_context(content)
|
||||
context['fcgid_path'] = fcgid_path % context
|
||||
fcgid = self.get_alias_directives(content)
|
||||
fcgid = self.get_static_directives(content)
|
||||
fcgid += textwrap.dedent("""\
|
||||
ProxyPass %(location)s !
|
||||
<Directory %(app_path)s>
|
||||
|
|
|
@ -5,18 +5,12 @@ from django.db import models
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core import validators, services
|
||||
from orchestra.utils import tuple_setting_to_choices
|
||||
from orchestra.utils.functional import cached
|
||||
|
||||
from . import settings
|
||||
|
||||
|
||||
def settings_to_choices(choices):
|
||||
return sorted(
|
||||
[ (name, opt[0]) for name,opt in choices.iteritems() ],
|
||||
key=lambda e: e[0]
|
||||
)
|
||||
|
||||
|
||||
class Website(models.Model):
|
||||
name = models.CharField(_("name"), max_length=128, unique=True,
|
||||
validators=[validators.validate_name])
|
||||
|
@ -67,7 +61,7 @@ class WebsiteOption(models.Model):
|
|||
website = models.ForeignKey(Website, verbose_name=_("web site"),
|
||||
related_name='options')
|
||||
name = models.CharField(_("name"), max_length=128,
|
||||
choices=settings_to_choices(settings.WEBSITES_OPTIONS))
|
||||
choices=tuple_setting_to_choices(settings.WEBSITES_OPTIONS))
|
||||
value = models.CharField(_("value"), max_length=256)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -2,6 +2,7 @@ from django import forms
|
|||
from django.contrib.auth import forms as auth_forms
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
|
||||
from .. import settings
|
||||
from ..core.validators import validate_password
|
||||
|
||||
|
||||
|
@ -51,8 +52,8 @@ class UserCreationForm(forms.ModelForm):
|
|||
# self.fields['password1'].validators.append(validate_password)
|
||||
|
||||
def clean_password2(self):
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
password1 = self.cleaned_data.get('password1')
|
||||
password2 = self.cleaned_data.get('password2')
|
||||
if password1 and password2 and password1 != password2:
|
||||
raise forms.ValidationError(
|
||||
self.error_messages['password_mismatch'],
|
||||
|
@ -72,7 +73,10 @@ class UserCreationForm(forms.ModelForm):
|
|||
|
||||
def save(self, commit=True):
|
||||
user = super(UserCreationForm, self).save(commit=False)
|
||||
user.set_password(self.cleaned_data["password1"])
|
||||
if settings.ORCHESTRA_MIGRATION_MODE:
|
||||
user.password = self.cleaned_data['password1']
|
||||
else:
|
||||
user.set_password(self.cleaned_data['password1'])
|
||||
if commit:
|
||||
user.save()
|
||||
return user
|
||||
|
|
|
@ -28,3 +28,6 @@ STOP_SERVICES = getattr(settings, 'STOP_SERVICES',
|
|||
|
||||
|
||||
API_ROOT_VIEW = getattr(settings, 'API_ROOT_VIEW', 'orchestra.api.root.APIRoot')
|
||||
|
||||
|
||||
ORCHESTRA_MIGRATION_MODE = getattr(settings, 'ORCHESTRA_MIGRATION_MODE', False)
|
||||
|
|
|
@ -128,3 +128,39 @@ def naturaldate(date):
|
|||
count = abs(count)
|
||||
fmt = pluralizefun(count)
|
||||
return fmt.format(num=count, ago=ago)
|
||||
|
||||
|
||||
def text2int(textnum, numwords={}):
|
||||
if not numwords:
|
||||
units = (
|
||||
'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
|
||||
'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
|
||||
'sixteen', 'seventeen', 'eighteen', 'nineteen',
|
||||
)
|
||||
|
||||
tens = ('', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety')
|
||||
|
||||
scales = ['hundred', 'thousand', 'million', 'billion', 'trillion']
|
||||
|
||||
numwords['and'] = (1, 0)
|
||||
for idx, word in enumerate(units):
|
||||
numwords[word] = (1, idx)
|
||||
for idx, word in enumerate(tens):
|
||||
numwords[word] = (1, idx * 10)
|
||||
for idx, word in enumerate(scales):
|
||||
numwords[word] = (10 ** (idx * 3 or 2), 0)
|
||||
|
||||
current = result = 0
|
||||
for word in textnum.split():
|
||||
word = word.lower()
|
||||
if word not in numwords:
|
||||
raise Exception("Illegal word: " + word)
|
||||
|
||||
scale, increment = numwords[word]
|
||||
current = current * scale + increment
|
||||
if scale > 100:
|
||||
result += current
|
||||
current = 0
|
||||
|
||||
return result + current
|
||||
|
||||
|
|
|
@ -45,3 +45,17 @@ def running_syncdb():
|
|||
|
||||
def database_ready():
|
||||
return not running_syncdb() and 'setuppostgres' not in sys.argv and 'test' not in sys.argv
|
||||
|
||||
|
||||
def dict_setting_to_choices(choices):
|
||||
return sorted(
|
||||
[ (name, opt.get('verbose_name', 'name')) for name, opt in choices.iteritems() ],
|
||||
key=lambda e: e[0]
|
||||
)
|
||||
|
||||
|
||||
def tuple_setting_to_choices(choices):
|
||||
return sorted(
|
||||
[ (name, opt[0]) for name,opt in choices.iteritems() ],
|
||||
key=lambda e: e[0]
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue