<commit_before>import collections

from django import forms
from django.contrib import admin
from django.contrib.admin import widgets
from django.forms.models import BaseInlineFormSet

from nodeconductor_assembly_waldur.packages import models


class GBtoMBWidget(widgets.AdminIntegerFieldWidget):

    def value_from_datadict(self, data, files, name):
        value = super(GBtoMBWidget, self).value_from_datadict(data, files, name)
        value = int(value) * 1024
        return value

    def _format_value(self, value):
        return int(value) / 1024

    def render(self, name, value, attrs=None):
        result = super(GBtoMBWidget, self).render(name, value, attrs)
        return '<label>%s GB</label>' % result


class PackageComponentForm(forms.ModelForm):
    monthly_price = forms.DecimalField(label="Price for 30 days", initial=0, required=True)

    class Meta:
        model = models.PackageComponent
        fields = ('type', 'amount', 'monthly_price', 'price')

    def __init__(self, *args, **kwargs):
        super(PackageComponentForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['monthly_price'].initial = self.instance.monthly_price

    def clean(self):
        super(PackageComponentForm, self).clean()
        if 'monthly_price' not in self.cleaned_data or 'amount' not in self.cleaned_data:
            return

        if self.instance.template.archived and 'monthly_price' in self.changed_data:
            raise forms.ValidationError('price cannot be changed for "archived" template')

        type = self.cleaned_data['type']
        monthly_price = self.cleaned_data['monthly_price']
        amount = self.cleaned_data['amount']

        price_min = 10 ** -models.PackageComponent.PRICE_DECIMAL_PLACES
        monthly_price_min = price_min * 30 * amount
        if monthly_price < monthly_price_min:
            raise forms.ValidationError('Monthly price for "%s" should be greater than %s' % (type, monthly_price_min))

        price_max = 10 ** (models.PackageComponent.PRICE_MAX_DIGITS - models.PackageComponent.PRICE_DECIMAL_PLACES)
        monthly_price_max = price_max * 30 * amount
        if monthly_price > monthly_price_max:
            raise forms.ValidationError('Monthly price for "%s" should be lower than %s' % (type, monthly_price_max))

    def save(self, commit=True):
        monthly_price = self.cleaned_data.get('monthly_price', None)
        amount = self.cleaned_data.get('amount', 0)
        package_component = super(PackageComponentForm, self).save(commit=commit)
        if amount:
            package_component.price = monthly_price / 30 / amount
        if commit:
            package_component.save()
        return package_component


class PackageComponentInlineFormset(BaseInlineFormSet):
    """
    Formset responsible for package component inlines validation and their initial population.
    """
    form = PackageComponentForm

    def __init__(self, **kwargs):
        # Fill inlines with required component types
        kwargs['initial'] = [{'type': t} for t in models.PackageTemplate.get_required_component_types()]
        super(PackageComponentInlineFormset, self).__init__(**kwargs)

    def add_fields(self, form, index):
        super(PackageComponentInlineFormset, self).add_fields(form, index)
        if 'type' in form.initial and form.initial['type'] in models.PackageTemplate.get_memory_types():
            form.fields['amount'] = forms.IntegerField(min_value=0, initial=0, widget=GBtoMBWidget())

    def clean(self):
        super(PackageComponentInlineFormset, self).clean()
        if any(self._errors):
            return
        # Check whether all required components were filled and are not marked for deletion.
        marked_for_delete = []
        filled = []
        for comp in self.cleaned_data:
            if not comp:
                continue
            elif comp.get('DELETE', False):
                marked_for_delete.append(comp['type'])
            filled.append(comp['type'])
        duplicates = [item for item, count in collections.Counter(filled).items() if count > 1]
        if duplicates:
            raise forms.ValidationError('One or more items are duplicated: %s' % ', '.join(duplicates))

        for t in models.PackageTemplate.get_required_component_types():
            if t not in filled:
                raise forms.ValidationError('%s component must be specified.' % t.capitalize())
            elif t in marked_for_delete:
                raise forms.ValidationError('%s component is required and cannot be deleted.' % t.capitalize())


class PackageComponentInline(admin.TabularInline):
    form = PackageComponentForm
    formset = PackageComponentInlineFormset
    model = models.PackageComponent
    extra = 0
    readonly_fields = ('price', )

    def get_extra(self, request, obj=None, **kwargs):
        if obj:
            return super(PackageComponentInline, self).get_extra(request, obj, **kwargs)
        # On creation number of inlines must be equal to number of required components
        return len(models.PackageTemplate.get_required_component_types())


class PackageTemplateAdmin(admin.ModelAdmin):
    inlines = [PackageComponentInline]
    fields = ('name', 'category', 'description', 'archived', 'icon_url', 'service_settings')
    list_display = ('name', 'uuid', 'service_settings', 'price', 'archived', 'monthly_price', 'category')
    list_filter = ('service_settings',)
    search_fields = ('name', 'uuid')


class OpenStackPackageAdmin(admin.ModelAdmin):
    list_display = ('template', 'tenant', 'service_settings')
    list_filter = ('template',)


admin.site.register(models.PackageTemplate, PackageTemplateAdmin)
admin.site.register(models.OpenStackPackage, OpenStackPackageAdmin)
<commit_msg>Change package template price validation [WAL-390]<commit_after>import collections

from django import forms
from django.contrib import admin
from django.contrib.admin import widgets
from django.forms.models import BaseInlineFormSet

from nodeconductor_assembly_waldur.packages import models


class GBtoMBWidget(widgets.AdminIntegerFieldWidget):

    def value_from_datadict(self, data, files, name):
        value = super(GBtoMBWidget, self).value_from_datadict(data, files, name)
        value = int(value) * 1024
        return value

    def _format_value(self, value):
        return int(value) / 1024

    def render(self, name, value, attrs=None):
        result = super(GBtoMBWidget, self).render(name, value, attrs)
        return '<label>%s GB</label>' % result


class PackageComponentForm(forms.ModelForm):
    monthly_price = forms.DecimalField(label="Price for 30 days", initial=0, required=True)

    class Meta:
        model = models.PackageComponent
        fields = ('type', 'amount', 'monthly_price', 'price')

    def __init__(self, *args, **kwargs):
        super(PackageComponentForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['monthly_price'].initial = self.instance.monthly_price

    def clean(self):
        super(PackageComponentForm, self).clean()
        if 'monthly_price' not in self.cleaned_data or 'amount' not in self.cleaned_data:
            return

        if self.instance.template.openstack_packages.first() and 'monthly_price' in self.changed_data:
            raise forms.ValidationError('Price cannot be changed for a template which has connected packages.')

        type = self.cleaned_data['type']
        monthly_price = self.cleaned_data['monthly_price']
        amount = self.cleaned_data['amount']

        price_min = 10 ** -models.PackageComponent.PRICE_DECIMAL_PLACES
        monthly_price_min = price_min * 30 * amount
        if monthly_price < monthly_price_min:
            raise forms.ValidationError('Monthly price for "%s" should be greater than %s' % (type, monthly_price_min))

        price_max = 10 ** (models.PackageComponent.PRICE_MAX_DIGITS - models.PackageComponent.PRICE_DECIMAL_PLACES)
        monthly_price_max = price_max * 30 * amount
        if monthly_price > monthly_price_max:
            raise forms.ValidationError('Monthly price for "%s" should be lower than %s' % (type, monthly_price_max))

    def save(self, commit=True):
        monthly_price = self.cleaned_data.get('monthly_price', None)
        amount = self.cleaned_data.get('amount', 0)
        package_component = super(PackageComponentForm, self).save(commit=commit)
        if amount:
            package_component.price = monthly_price / 30 / amount
        if commit:
            package_component.save()
        return package_component


class PackageComponentInlineFormset(BaseInlineFormSet):
    """
    Formset responsible for package component inlines validation and their initial population.
    """
    form = PackageComponentForm

    def __init__(self, **kwargs):
        # Fill inlines with required component types
        kwargs['initial'] = [{'type': t} for t in models.PackageTemplate.get_required_component_types()]
        super(PackageComponentInlineFormset, self).__init__(**kwargs)

    def add_fields(self, form, index):
        super(PackageComponentInlineFormset, self).add_fields(form, index)
        if 'type' in form.initial and form.initial['type'] in models.PackageTemplate.get_memory_types():
            form.fields['amount'] = forms.IntegerField(min_value=0, initial=0, widget=GBtoMBWidget())

    def clean(self):
        super(PackageComponentInlineFormset, self).clean()
        if any(self._errors):
            return
        # Check whether all required components were filled and are not marked for deletion.
        marked_for_delete = []
        filled = []
        for comp in self.cleaned_data:
            if not comp:
                continue
            elif comp.get('DELETE', False):
                marked_for_delete.append(comp['type'])
            filled.append(comp['type'])
        duplicates = [item for item, count in collections.Counter(filled).items() if count > 1]
        if duplicates:
            raise forms.ValidationError('One or more items are duplicated: %s' % ', '.join(duplicates))

        for t in models.PackageTemplate.get_required_component_types():
            if t not in filled:
                raise forms.ValidationError('%s component must be specified.' % t.capitalize())
            elif t in marked_for_delete:
                raise forms.ValidationError('%s component is required and cannot be deleted.' % t.capitalize())


class PackageComponentInline(admin.TabularInline):
    form = PackageComponentForm
    formset = PackageComponentInlineFormset
    model = models.PackageComponent
    extra = 0
    readonly_fields = ('price', )

    def get_extra(self, request, obj=None, **kwargs):
        if obj:
            return super(PackageComponentInline, self).get_extra(request, obj, **kwargs)
        # On creation number of inlines must be equal to number of required components
        return len(models.PackageTemplate.get_required_component_types())


class PackageTemplateAdmin(admin.ModelAdmin):
    inlines = [PackageComponentInline]
    fields = ('name', 'category', 'description', 'archived', 'icon_url', 'service_settings')
    list_display = ('name', 'uuid', 'service_settings', 'price', 'archived', 'monthly_price', 'category')
    list_filter = ('service_settings',)
    search_fields = ('name', 'uuid')


class OpenStackPackageAdmin(admin.ModelAdmin):
    list_display = ('template', 'tenant', 'service_settings')
    list_filter = ('template',)


admin.site.register(models.PackageTemplate, PackageTemplateAdmin)
admin.site.register(models.OpenStackPackage, OpenStackPackageAdmin)

{'fraction_non_alphanumeric': 0.10082375150163034, 'fraction_numerical': 0.004719409644757165, 'mean_word_length': 3.386526157320286, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2269503', 'n_tokens_mistral': 3229, 'n_tokens_neox': 3148, 'n_words': 821}