예제 #1
0
 def aggregate_history(self, dataset):
     prev = None
     prev_object_id = None
     datas = []
     sink = AttrDict(object_id=-1, value=-1, content_object_repr='',
         created_at=AttrDict(year=-1, month=-1))
     for mdata in itertools.chain(dataset.order_by('object_id', 'created_at'), [sink]):
         object_id = mdata.object_id
         ymonth = (mdata.created_at.year, mdata.created_at.month)
         if object_id != prev_object_id or ymonth != prev.ymonth:
             if prev_object_id is not None:
                 data = AttrDict(
                     date=datetime.date(
                         year=prev.ymonth[0],
                         month=prev.ymonth[1],
                         day=1
                     ),
                     value=current,
                     content_object_repr=prev.content_object_repr
                 )
                 datas.append(data)
             current = mdata.value
         else:
             current += mdata.value
         if object_id != prev_object_id:
             if prev_object_id is not None:
                 yield (prev.content_object_repr, datas)
                 datas = []
         prev = mdata
         prev.ymonth = ymonth
         prev_object_id = object_id
예제 #2
0
 def get_records(self):
     types = set()
     records = utils.RecordStorage()
     for record in self.get_declared_records():
         types.add(record.type)
         if record.type == record.SOA:
             # Update serial and insert at 0
             value = record.value.split()
             value[2] = str(self.serial)
             records.insert(
                 0,
                 AttrDict(type=record.SOA,
                          ttl=record.get_ttl(),
                          value=' '.join(value)))
         else:
             records.append(
                 AttrDict(type=record.type,
                          ttl=record.get_ttl(),
                          value=record.value))
     for record in self.get_default_records():
         if self.record_is_implicit(record, types):
             if record.type is Record.SOA:
                 records.insert(0, record)
             else:
                 records.append(record)
     return records
예제 #3
0
    def __init__(self, *args, **kwargs):
        super(MailboxForm, self).__init__(*args, **kwargs)
        # Hack the widget in order to display add button
        remote_field_mock = AttrDict(
            **{
                'model': Address,
                'get_related_field': lambda: AttrDict(name='id'),
            })
        widget = self.fields['addresses'].widget
        self.fields['addresses'].widget = widgets.RelatedFieldWidgetWrapper(
            widget,
            remote_field_mock,
            self.modeladmin.admin_site,
            can_add_related=True)

        account = self.modeladmin.account
        # Filter related addresses by account
        old_render = self.fields['addresses'].widget.render

        def render(*args, **kwargs):
            output = old_render(*args, **kwargs)
            args = 'account=%i&mailboxes=%s' % (account.pk, self.instance.pk)
            output = output.replace('/add/?', '/add/?%s&' % args)
            return mark_safe(output)

        self.fields['addresses'].widget.render = render
        queryset = self.fields['addresses'].queryset
        realted_addresses = queryset.filter(
            account_id=account.pk).order_by('name')
        self.fields['addresses'].queryset = realted_addresses

        if self.instance and self.instance.pk:
            self.fields['addresses'].initial = self.instance.addresses.all()
예제 #4
0
def best_price(rates, metric):
    if rates.query.order_by != ['plan', 'quantity']:
        raise ValueError(
            "rates queryset should be ordered by 'plan' and 'quantity'")
    candidates = []
    for plan, rates in rates.group_by('plan').items():
        rates = _standardize(rates)
        plan_candidates = []
        for rate in rates:
            if rate.quantity > metric:
                break
            if plan_candidates:
                ant = plan_candidates[-1]
                if ant.price == rate.price:
                    # Multiple plans support
                    ant.fold += 1
                else:
                    ant.quantity = rate.quantity - 1
                    plan_candidates.append(
                        AttrDict(
                            price=rate.price,
                            quantity=metric,
                            fold=1,
                        ))
            else:
                plan_candidates.append(
                    AttrDict(
                        price=rate.price,
                        quantity=metric,
                        fold=1,
                    ))
        candidates.extend(plan_candidates)
    results = []
    accumulated = 0
    for candidate in sorted(candidates, key=lambda c: c.price):
        if candidate.quantity < accumulated:
            # Out of barrier
            continue
        candidate.quantity *= candidate.fold
        if accumulated + candidate.quantity > metric:
            quantity = metric - accumulated
        else:
            quantity = candidate.quantity
        accumulated += quantity
        if quantity:
            if results and results[-1].price == candidate.price:
                results[-1].quantity += quantity
            else:
                results.append(
                    AttrDict(**{
                        'quantity': quantity,
                        'price': candidate.price
                    }))
    return results
예제 #5
0
def match_price(rates, metric):
    if rates.query.order_by != ['plan', 'quantity']:
        raise ValueError(
            "rates queryset should be ordered by 'plan' and 'quantity'")
    candidates = []
    selected = False
    prev = None
    rates = _standardize(rates.distinct())
    for rate in rates:
        if prev:
            if prev.plan != rate.plan:
                if not selected and prev.quantity <= metric:
                    candidates.append(prev)
                selected = False
            if not selected and rate.quantity > metric:
                if prev.quantity <= metric:
                    candidates.append(prev)
                    selected = True
        prev = rate
    if not selected and prev.quantity <= metric:
        candidates.append(prev)
    candidates.sort(key=lambda r: r.price)
    if candidates:
        return [
            AttrDict(**{
                'quantity': metric,
                'price': candidates[0].price,
            })
        ]
    return None
예제 #6
0
 def load(cls, operation, log=None):
     routes = None
     if log:
         routes = {
             (operation.backend, operation.action): AttrDict(host=log.server)
         }
     return cls(operation.backend_class, operation.instance, operation.action, routes=routes)
예제 #7
0
 def get_default_records(self):
     defaults = []
     if self.is_top:
         for ns in settings.DOMAINS_DEFAULT_NS:
             defaults.append(AttrDict(type=Record.NS, value=ns))
         soa = self.get_default_soa()
         defaults.insert(0, AttrDict(type=Record.SOA, value=soa))
     for mx in settings.DOMAINS_DEFAULT_MX:
         defaults.append(AttrDict(type=Record.MX, value=mx))
     default_a = settings.DOMAINS_DEFAULT_A
     if default_a:
         defaults.append(AttrDict(type=Record.A, value=default_a))
     default_aaaa = settings.DOMAINS_DEFAULT_AAAA
     if default_aaaa:
         defaults.append(AttrDict(type=Record.AAAA, value=default_aaaa))
     return defaults
예제 #8
0
def step_price(rates, metric):
    if rates.query.order_by != ['plan', 'quantity']:
        raise ValueError(
            "rates queryset should be ordered by 'plan' and 'quantity'")
    # Step price
    group = []
    minimal = (sys.maxsize, [])
    for plan, rates in rates.group_by('plan').items():
        rates = _standardize(rates)
        value, steps = _compute_steps(rates, metric)
        if plan.is_combinable:
            group.append(steps)
        else:
            minimal = min(minimal, (value, steps), key=lambda v: v[0])
    if len(group) == 1:
        value, steps = _compute_steps(rates, metric)
        minimal = min(minimal, (value, steps), key=lambda v: v[0])
    elif len(group) > 1:
        # Merge
        steps = []
        for rates in group:
            steps += rates
        steps.sort(key=lambda s: s.price)
        result = []
        counter = 0
        value = 0
        ix = 0
        targets = []
        while counter < metric:
            barrier = steps[ix].barrier
            if barrier <= counter + 1:
                price = steps[ix].price
                quantity = steps[ix].quantity
                if quantity + counter > metric:
                    quantity = metric - counter
                else:
                    for target in targets:
                        if counter + quantity >= target:
                            quantity = (counter + quantity + 1) - target
                            steps[ix].quantity -= quantity
                            if not steps[ix].quantity:
                                steps.pop(ix)
                            break
                    else:
                        steps.pop(ix)
                counter += quantity
                value += quantity * price
                if result and result[-1].price == price:
                    result[-1].quantity += quantity
                else:
                    result.append(AttrDict(quantity=quantity, price=price))
                ix = 0
                targets = []
            else:
                targets.append(barrier)
                ix += 1
        minimal = min(minimal, (value, result), key=lambda v: v[0])
    return minimal[1]
예제 #9
0
 def inner(fn, name, method, *args, **kwargs):
     task_id = get_id()
     kwargs.update({
         '_name': name,
         '_task_id': task_id,
     })
     thread = method(target=fn, args=args, kwargs=kwargs)
     thread.start()
     # Celery API compat
     thread.request = AttrDict(id=task_id)
     return thread
예제 #10
0
    def generate_line(self,
                      order,
                      price,
                      *dates,
                      metric=1,
                      discounts=None,
                      computed=False):
        """
        discounts: extra discounts to apply
        computed: price = price*size already performed
        """
        if len(dates) == 2:
            ini, end = dates
        elif len(dates) == 1:
            ini, end = dates[0], dates[0]
        else:
            raise AttributeError("WTF is '%s'?" % dates)
        discounts = discounts or ()

        size = self.get_price_size(ini, end)
        if not computed:
            price = price * size
        subtotal = self.nominal_price * size * metric
        line = AttrDict(
            **{
                'order': order,
                'subtotal': subtotal,
                'ini': ini,
                'end': end,
                'size': size,
                'metric': metric,
                'discounts': [],
            })

        if subtotal > price:
            plan_discount = price - subtotal
            self.generate_discount(line, self._PLAN, plan_discount)
            subtotal += plan_discount
        for dtype, dprice in discounts:
            subtotal += dprice
            # Prevent compensations/prepays to refund money
            if dtype in (self._COMPENSATION, self._PREPAY) and subtotal < 0:
                dprice -= subtotal
            if dprice:
                self.generate_discount(line, dtype, dprice)
        return line
예제 #11
0
def _compute_steps(rates, metric):
    value = 0
    num = len(rates)
    accumulated = 0
    barrier = 1
    next_barrier = None
    end = False
    ix = 0
    steps = []
    while ix < num and not end:
        fold = 1
        # Multiple contractions
        while ix < num - 1 and rates[ix] == rates[ix + 1]:
            ix += 1
            fold += 1
        if ix + 1 == num:
            quantity = metric - accumulated
            next_barrier = quantity
        else:
            quantity = rates[ix + 1].quantity - max(rates[ix].quantity, 1)
            next_barrier = quantity
            if rates[ix + 1].price > rates[ix].price:
                quantity *= fold
            if accumulated + quantity > metric:
                quantity = metric - accumulated
                end = True
        price = rates[ix].price
        steps.append(
            AttrDict(**{
                'quantity': quantity,
                'price': price,
                'barrier': barrier,
            }))
        accumulated += quantity
        barrier += next_barrier
        value += quantity * price
        ix += 1
    return value, steps
예제 #12
0
 def generate_discount(self, line, dtype, price):
     line.discounts.append(AttrDict(**{
         'type': dtype,
         'total': price,
     }))
예제 #13
0
def edit_records(modeladmin, request, queryset):
    selected_ids = queryset.values_list('id', flat=True)
    # Include subodmains
    queryset = queryset.model.objects.filter(
        Q(top__id__in=selected_ids) | Q(id__in=selected_ids)).annotate(
            structured_id=Coalesce('top__id', 'id'),
            structured_name=Concat('top__name',
                                   'name')).order_by('-structured_id',
                                                     'structured_name')
    formsets = []
    for domain in queryset.prefetch_related('records'):
        modeladmin_copy = copy.copy(modeladmin)
        modeladmin_copy.model = Record
        prefix = '' if domain.is_top else '&nbsp;' * 8
        context = {
            'url': change_url(domain),
            'name': prefix + domain.name,
            'title': '',
        }
        if domain.id not in selected_ids:
            context['name'] += '*'
            context['title'] = _(
                "This subdomain was not explicitly selected "
                "but has been automatically added to this list.")
        link = '<a href="%(url)s" title="%(title)s">%(name)s</a>' % context
        modeladmin_copy.verbose_name_plural = mark_safe(link)
        RecordFormSet = modelformset_factory(Record,
                                             form=RecordForm,
                                             formset=RecordEditFormSet,
                                             extra=1,
                                             can_delete=True)
        formset = RecordFormSet(queryset=domain.records.all(),
                                prefix=domain.id)
        formset.instance = domain
        formset.cls = RecordFormSet
        formsets.append(formset)

    if request.POST.get('post') == 'generic_confirmation':
        posted_formsets = []
        all_valid = True
        for formset in formsets:
            instance = formset.instance
            formset = formset.cls(request.POST,
                                  request.FILES,
                                  queryset=formset.queryset,
                                  prefix=instance.id)
            formset.instance = instance
            if not formset.is_valid():
                all_valid = False
            posted_formsets.append(formset)
        formsets = posted_formsets
        if all_valid:
            for formset in formsets:
                for form in formset.forms:
                    form.instance.domain_id = formset.instance.id
                formset.save()
                fake_form = AttrDict({'changed_data': False})
                change_message = modeladmin.construct_change_message(
                    request, fake_form, [formset])
                modeladmin.log_change(request, formset.instance,
                                      change_message)
            num = len(formsets)
            message = ungettext(
                _("Records for one selected domain have been updated."),
                _("Records for %i selected domains have been updated.") % num,
                num)
            modeladmin.message_user(request, message)
            return

    opts = modeladmin.model._meta
    context = {
        'title': _("Edit records"),
        'action_name': _("Edit records"),
        'action_value': 'edit_records',
        'display_objects': [],
        'queryset': queryset,
        'opts': opts,
        'app_label': opts.app_label,
        'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
        'formsets': formsets,
        'obj': get_object_from_url(modeladmin, request),
    }
    return render(request, 'admin/domains/domain/edit_records.html', context)
예제 #14
0
def clean_custom_url(saas):
    instance = saas.instance
    instance.custom_url = instance.custom_url.strip()
    url = urlparse(instance.custom_url)
    if not url.path:
        instance.custom_url += '/'
        url = urlparse(instance.custom_url)
    try:
        protocol, valid_protocols = saas.PROTOCOL_MAP[url.scheme]
    except KeyError:
        raise ValidationError({
            'custom_url':
            _("%s scheme not supported (http/https)") % url.scheme,
        })
    account = instance.account
    # get or create website
    try:
        website = Website.objects.get(
            protocol__in=valid_protocols,
            domains__name=url.netloc,
            account=account,
        )
    except Website.DoesNotExist:
        # get or create domain
        Domain = Website.domains.field.related_model
        try:
            domain = Domain.objects.get(name=url.netloc)
        except Domain.DoesNotExist:
            raise ValidationError({
                'custom_url':
                _("Domain %s does not exist.") % url.netloc,
            })
        if domain.account != account:
            raise ValidationError({
                'custom_url':
                _("Domain %s does not belong to account %s, it's from %s.") %
                (url.netloc, account, domain.account),
            })
        # Create new website for custom_url
        # Changed by daniel: hardcode target_server to web.pangea.lan, consider putting it into settings.py
        tgt_server = Server.objects.get(name='web.pangea.lan')
        website = Website(name=url.netloc,
                          protocol=protocol,
                          account=account,
                          target_server=tgt_server)
        full_clean(website)
        try:
            validate_domain_protocol(website, domain, protocol)
        except ValidationError as e:
            raise ValidationError({
                'custom_url':
                _("Error validating related %s: %s") %
                (type(website).__name__, e),
            })
    # get or create directive
    try:
        directive = website.directives.get(name=saas.get_directive_name())
    except WebsiteDirective.DoesNotExist:
        directive = WebsiteDirective(name=saas.get_directive_name(),
                                     value=url.path)
    if not directive.pk or directive.value != url.path:
        directive.value = url.path
        if website.pk:
            directive.website = website
            full_clean(directive)
            # Adaptation of orchestra.websites.forms.WebsiteDirectiveInlineFormSet.clean()
            locations = set(
                Content.objects.filter(website=website).values_list('path',
                                                                    flat=True))
            values = defaultdict(list)
            directives = WebsiteDirective.objects.filter(website=website)
            for wdirective in directives.exclude(pk=directive.pk):
                fdirective = AttrDict({
                    'name': wdirective.name,
                    'value': wdirective.value
                })
                try:
                    wdirective.directive_instance.validate_uniqueness(
                        fdirective, values, locations)
                except ValidationError as err:
                    raise ValidationError({
                        'custom_url':
                        _("Another directive with this URL path exists (%s)." %
                          err)
                    })
        else:
            full_clean(directive, exclude=('website', ))
    return directive