<s> from __future__ import unicode_literals <EOL> import functools <EOL> import inspect <EOL> from collections import defaultdict <EOL> from django . contrib . contenttypes import fields as ct_fields <EOL> from django . contrib . contenttypes import models as ct_models <EOL> from django . db import models <EOL> from django . db . models import Sum <EOL> from django . utils import six <EOL> from django . utils . encoding import python_2_unicode_compatible <EOL> from model_utils import FieldTracker <EOL> import reversion <EOL> from nodeconductor . logging . loggers import LoggableMixin <EOL> from nodeconductor . logging . models import AlertThresholdMixin <EOL> from nodeconductor . quotas import exceptions , managers , fields <EOL> from nodeconductor . core . models import UuidMixin , ReversionMixin , DescendantMixin <EOL> @ python_2_unicode_compatible <EOL> @ reversion . register ( fields = [ '<STR_LIT>' , '<STR_LIT>' ] ) <EOL> class Quota ( UuidMixin , AlertThresholdMixin , LoggableMixin , ReversionMixin , models . Model ) : <EOL> """<STR_LIT>""" <EOL> class Meta : <EOL> unique_together = ( ( '<STR_LIT:name>' , '<STR_LIT>' , '<STR_LIT>' ) , ) <EOL> limit = models . FloatField ( default = - <NUM_LIT:1> ) <EOL> usage = models . FloatField ( default = <NUM_LIT:0> ) <EOL> name = models . CharField ( max_length = <NUM_LIT> , db_index = True ) <EOL> content_type = models . ForeignKey ( ct_models . ContentType , null = True ) <EOL> object_id = models . PositiveIntegerField ( null = True ) <EOL> scope = ct_fields . GenericForeignKey ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> objects = managers . QuotaManager ( '<STR_LIT>' ) <EOL> tracker = FieldTracker ( ) <EOL> def __str__ ( self ) : <EOL> return '<STR_LIT>' % ( self . name , self . scope ) <EOL> def is_exceeded ( self , delta = None , threshold = None ) : <EOL> """<STR_LIT>""" <EOL> if self . limit == - <NUM_LIT:1> : <EOL> return False <EOL> usage = self . usage <EOL> limit = self . limit <EOL> if delta is not None : <EOL> usage += delta <EOL> if threshold is not None : <EOL> limit = threshold * limit <EOL> return usage > limit <EOL> def get_log_fields ( self ) : <EOL> return ( '<STR_LIT>' , '<STR_LIT:name>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ) <EOL> def get_field ( self ) : <EOL> fields = self . scope . get_quotas_fields ( ) <EOL> try : <EOL> return next ( f for f in fields if f . name == self . name ) <EOL> except StopIteration : <EOL> return <EOL> def is_over_threshold ( self ) : <EOL> return self . usage >= self . threshold <EOL> def _fail_silently ( method ) : <EOL> @ functools . wraps ( method ) <EOL> def wrapped ( self , quota_name , * args , ** kwargs ) : <EOL> try : <EOL> return method ( self , quota_name , * args , ** kwargs ) <EOL> except Quota . DoesNotExist : <EOL> if not kwargs . get ( '<STR_LIT>' , False ) : <EOL> raise Quota . DoesNotExist ( '<STR_LIT>' % ( self , quota_name ) ) <EOL> return wrapped <EOL> class QuotaModelMixin ( models . Model ) : <EOL> """<STR_LIT>""" <EOL> QUOTAS_NAMES = [ ] <EOL> class Quotas ( six . with_metaclass ( fields . FieldsContainerMeta ) ) : <EOL> enable_fields_caching = True <EOL> class Meta : <EOL> abstract = True <EOL> quotas = ct_fields . GenericRelation ( '<STR_LIT>' , related_query_name = '<STR_LIT>' ) <EOL> @ _fail_silently <EOL> def set_quota_limit ( self , quota_name , limit , fail_silently = False ) : <EOL> quota = self . quotas . get ( name = quota_name ) <EOL> quota . limit = limit <EOL> quota . save ( ) <EOL> @ _fail_silently <EOL> def set_quota_usage ( self , quota_name , usage , fail_silently = False ) : <EOL> quota = self . quotas . get ( name = quota_name ) <EOL> quota . usage = usage <EOL> quota . save ( ) <EOL> @ _fail_silently <EOL> def add_quota_usage ( self , quota_name , usage_delta , fail_silently = False ) : <EOL> quota = self . quotas . get ( name = quota_name ) <EOL> quota . usage += usage_delta <EOL> quota . save ( ) <EOL> def get_quota_ancestors ( self ) : <EOL> if isinstance ( self , DescendantMixin ) : <EOL> return [ a for a in self . get_ancestors ( ) if isinstance ( a , QuotaModelMixin ) ] <EOL> return [ ] <EOL> def validate_quota_change ( self , quota_deltas , raise_exception = False ) : <EOL> """<STR_LIT>""" <EOL> errors = [ ] <EOL> for name , delta in quota_deltas . iteritems ( ) : <EOL> quota = self . quotas . get ( name = name ) <EOL> if quota . is_exceeded ( delta ) : <EOL> errors . append ( '<STR_LIT>' % ( <EOL> quota . name , quota . limit , quota . usage + delta , quota . scope ) ) <EOL> if not raise_exception : <EOL> return errors <EOL> else : <EOL> if errors : <EOL> raise exceptions . QuotaExceededException ( '<STR_LIT>' % '<STR_LIT:;>' . join ( errors ) ) <EOL> def can_user_update_quotas ( self , user ) : <EOL> """<STR_LIT>""" <EOL> return user . is_staff <EOL> @ classmethod <EOL> def get_sum_of_quotas_as_dict ( cls , scopes , quota_names = None , fields = [ '<STR_LIT>' , '<STR_LIT>' ] ) : <EOL> """<STR_LIT>""" <EOL> if not scopes : <EOL> return { } <EOL> if quota_names is None : <EOL> quota_names = cls . get_quotas_names ( ) <EOL> scope_models = set ( [ scope . _meta . model for scope in scopes ] ) <EOL> if len ( scope_models ) > <NUM_LIT:1> : <EOL> raise exceptions . QuotaError ( '<STR_LIT>' ) <EOL> filter_kwargs = { <EOL> '<STR_LIT>' : ct_models . ContentType . objects . get_for_model ( scopes [ <NUM_LIT:0> ] ) , <EOL> '<STR_LIT>' : [ scope . id for scope in scopes ] , <EOL> '<STR_LIT>' : quota_names <EOL> } <EOL> result = { } <EOL> if '<STR_LIT>' in fields : <EOL> items = Quota . objects . filter ( ** filter_kwargs ) . values ( '<STR_LIT:name>' ) . annotate ( usage = Sum ( '<STR_LIT>' ) ) <EOL> for item in items : <EOL> result [ item [ '<STR_LIT:name>' ] + '<STR_LIT>' ] = item [ '<STR_LIT>' ] <EOL> if '<STR_LIT>' in fields : <EOL> unlimited_quotas = Quota . objects . filter ( limit = - <NUM_LIT:1> , ** filter_kwargs ) <EOL> unlimited_quotas = list ( unlimited_quotas . values_list ( '<STR_LIT:name>' , flat = True ) ) <EOL> for quota_name in unlimited_quotas : <EOL> result [ quota_name ] = - <NUM_LIT:1> <EOL> items = Quota . objects . filter ( ** filter_kwargs ) . exclude ( name__in = unlimited_quotas ) . values ( '<STR_LIT:name>' ) . annotate ( limit = Sum ( '<STR_LIT>' ) ) <EOL> for item in items : <EOL> result [ item [ '<STR_LIT:name>' ] ] = item [ '<STR_LIT>' ] <EOL> return result <EOL> @ classmethod <EOL> def get_sum_of_quotas_for_querysets ( cls , querysets , quota_names = None ) : <EOL> partial_sums = [ qs . model . get_sum_of_quotas_as_dict ( qs , quota_names ) for qs in querysets ] <EOL> return reduce ( cls . _sum_dicts , partial_sums , defaultdict ( lambda : <NUM_LIT:0> ) ) <EOL> @ classmethod <EOL> def _sum_dicts ( cls , total , partial ) : <EOL> for key , val in partial . items ( ) : <EOL> if val != - <NUM_LIT:1> : <EOL> total [ key ] += val <EOL> for key in partial . keys ( ) : <EOL> if key not in total : <EOL> total [ key ] = - <NUM_LIT:1> <EOL> return total <EOL> @ classmethod <EOL> def get_quotas_fields ( cls , field_class = None ) : <EOL> if not hasattr ( cls , '<STR_LIT>' ) or not cls . Quotas . enable_fields_caching : <EOL> cls . _quota_fields = dict ( inspect . getmembers ( cls . Quotas , lambda m : isinstance ( m , fields . QuotaField ) ) ) . values ( ) <EOL> if field_class is not None : <EOL> return [ v for v in cls . _quota_fields if isinstance ( v , field_class ) ] <EOL> return cls . _quota_fields <EOL> @ classmethod <EOL> def get_quotas_names ( cls ) : <EOL> return cls . QUOTAS_NAMES + [ f . name for f in cls . get_quotas_fields ( ) ] <EOL> class ExtendableQuotaModelMixin ( QuotaModelMixin ) : <EOL> """<STR_LIT>""" <EOL> class Quotas ( QuotaModelMixin . Quotas ) : <EOL> enable_fields_caching = False <EOL> class Meta : <EOL> abstract = True <EOL> @ classmethod <EOL> def add_quota_field ( cls , name , quota_field ) : <EOL> quota_field . name = name <EOL> setattr ( cls . Quotas , name , quota_field ) <EOL> from nodeconductor . quotas . apps import QuotasConfig <EOL> if isinstance ( quota_field , fields . CounterQuotaField ) : <EOL> QuotasConfig . register_counter_field_signals ( model = cls , counter_field = quota_field ) </s>
