def get_list_queryset(self, queryset): lookup_params = dict([ (smart_str(k)[len(FILTER_PREFIX):], v) for k, v in self.admin_view.params.items() if smart_str(k).startswith(FILTER_PREFIX) and v != '' ]) for p_key, p_val in iteritems(lookup_params): if p_val == "False": lookup_params[p_key] = False use_distinct = False # for clean filters self.admin_view.has_query_param = bool(lookup_params) self.admin_view.clean_query_url = self.admin_view.get_query_string( remove=[ k for k in self.request.GET.keys() if k.startswith(FILTER_PREFIX) ]) # Normalize the types of keys if not self.free_query_filter: for key, value in lookup_params.items(): if not self.lookup_allowed(key, value): raise SuspiciousOperation("Filtering by %s not allowed" % key) self.filter_specs = [] if self.list_filter: for list_filter in self.list_filter: if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(self.request, lookup_params, self.model, self) else: field_path = None field_parts = [] if isinstance(list_filter, (tuple, list)): # This is a custom FieldListFilter class for a given field. field, field_list_filter_class = list_filter else: # This is simply a field name, so use the default # FieldListFilter class that has been registered for # the type of the given field. field, field_list_filter_class = list_filter, filter_manager.create if not isinstance(field, models.Field): field_path = field field_parts = get_fields_from_path( self.model, field_path) field = field_parts[-1] spec = field_list_filter_class(field, self.request, lookup_params, self.model, self.admin_view, field_path=field_path) if len(field_parts) > 1: # Add related model name to title spec.title = "%s %s" % (field_parts[-2].name, spec.title) # Check if we need to use distinct() use_distinct = (use_distinct or lookup_needs_distinct( self.opts, field_path)) if spec and spec.has_output(): try: new_qs = spec.do_filte(queryset) except ValidationError as e: new_qs = None self.admin_view.message_user( _("<b>Filtering error:</b> %s") % e.messages[0], 'error') if new_qs is not None: queryset = new_qs self.filter_specs.append(spec) self.has_filters = bool(self.filter_specs) self.admin_view.filter_specs = self.filter_specs obj = filter(lambda f: f.is_used, self.filter_specs) if six.PY3: obj = list(obj) self.admin_view.used_filter_num = len(obj) try: for key, value in lookup_params.items(): use_distinct = (use_distinct or lookup_needs_distinct(self.opts, key)) except FieldDoesNotExist as e: raise IncorrectLookupParameters(e) try: # fix a bug by david: In demo, quick filter by IDC Name() cannot be used. if isinstance(queryset, models.query.QuerySet) and lookup_params: new_lookup_parames = dict() for k, v in lookup_params.items(): list_v = v.split(',') if len(list_v) > 0: new_lookup_parames.update({k: list_v}) else: new_lookup_parames.update({k: v}) queryset = queryset.filter(**new_lookup_parames) except (SuspiciousOperation, ImproperlyConfigured): raise except Exception as e: raise IncorrectLookupParameters(e) else: if not isinstance(queryset, models.query.QuerySet): pass query = self.request.GET.get(SEARCH_VAR, '') # Apply keyword searches. def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name if self.search_fields and query: orm_lookups = [ construct_search(str(search_field)) for search_field in self.search_fields ] for bit in query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: if lookup_needs_distinct(self.opts, search_spec): use_distinct = True break self.admin_view.search_query = query if use_distinct: return queryset.distinct() else: return queryset
def search(self, query): return self.get_queryset().filter( models.Q(name__icontains=query) | \ models.Q(description__icontains=query) )
def get_context_data(self, **kwargs): context = super(ReportMargins, self).get_context_data(**kwargs) if self.form.is_valid(): venture = Venture.objects.get( id=self.form.cleaned_data['margin_venture']) query = HistoryCost.objects.filter( db.Q(venture=venture) | db.Q(venture__parent=venture) | db.Q(venture__parent__parent=venture) | db.Q(venture__parent__parent__parent=venture) | db.Q(venture__parent__parent__parent__parent=venture)) total_cost = 0 total_sim = 0 total_count = 0 for mk in self.margin_kinds: q = query.filter( db.Q(device__margin_kind=mk) | db.Q( db.Q(device__margin_kind=None) & db.Q( db.Q(device__venture__margin_kind=mk) | db.Q(device__venture__margin_kind=None, device__venture__parent__margin_kind=mk) | db. Q(device__venture__margin_kind=None, device__venture__parent__margin_kind=None, device__venture__parent__parent__margin_kind=mk) | db. Q(device__venture__margin_kind=None, device__venture__parent__margin_kind=None, device__venture__parent__parent__margin_kind=None, device__venture__parent__parent__parent__margin_kind =mk)))) mk.total, mk.count, mk.count_now = total_cost_count( q, self.form.cleaned_data['start'], self.form.cleaned_data['end'], ) mk.sim_margin = self.form.get('m_%d' % mk.id, 0) or 0 mk.sim_cost = ((mk.total or 0) / (1 + mk.margin / 100) * (1 + mk.sim_margin / 100)) total_sim += mk.sim_cost total_cost += mk.total or 0 total_count += mk.count or 0 context.update({ 'venture': venture, 'total_cost': total_cost, 'total_sim': total_sim, 'total_count': total_count, }) context.update({ 'form': self.form, 'margin_kinds': self.margin_kinds, 'zip_margin_kinds_form': zip([f for f in self.form if not f.label], self.margin_kinds), }) return context
def update_liabilites(self): src_account = SpecialAccounts.fees dst_account = SpecialAccounts.fees_receivable config = Configuration.get_solo() # Step 1: Identify all dates and amounts that should be due at those dates # (in python, store as a list; hits database once to get list of memberships) # Step 2: Find all due amounts within the data ranges, ignore reversed liabilities # (hits database) # Step 3: Compare due date and amounts with list from step 1 # (in Python) # Step 4: Cancel all liabilities that didn't match in step 3 # (hits database, once per mismatch) # Step 5: Add all missing liabilities # (hits database, once per new due) # Step 6: Find and cancel all liabilities outside of membership dates, replaces remove_future_liabilites_on_leave() # (hits database, once for search, once per stray liability) dues = set() membership_ranges = [] _now = now() _from = config.accounting_start # Step 1 for membership in self.memberships.all(): if not membership.amount: continue membership_range, membership_dues = membership.get_dues( _now=_now, _from=_from ) membership_ranges.append(membership_range) dues |= membership_dues # Step 2 dues_qs = Booking.objects.filter( member=self, credit_account=src_account, transaction__reversed_by__isnull=True, ) if membership_ranges: date_range_q = reduce( lambda a, b: a | b, [ models.Q(transaction__value_datetime__gte=start) & models.Q(transaction__value_datetime__lte=end) for start, end in membership_ranges ], ) dues_qs = dues_qs.filter(date_range_q) dues_in_db = { # Must be a dictionary instead of set, to retrieve b later on (b.transaction.value_datetime.date(), b.amount): b for b in dues_qs.all() } # Step 3 dues_in_db_as_set = set(dues_in_db.keys()) wrong_dues_in_db = dues_in_db_as_set - dues missing_dues = dues - dues_in_db_as_set # Step 4 for wrong_due in sorted(wrong_dues_in_db): booking = dues_in_db[wrong_due] booking.transaction.reverse( memo=_("Due amount canceled because of change in membership amount"), user_or_context="internal: update_liabilites, membership amount changed", ) # Step 5: for (date, amount) in sorted(missing_dues): t = Transaction.objects.create( value_datetime=date, booking_datetime=_now, memo=_("Membership due"), user_or_context="internal: update_liabilites, add missing liabilities", ) t.credit( account=src_account, amount=amount, member=self, user_or_context="internal: update_liabilites, add missing liabilities", ) t.debit( account=dst_account, amount=amount, member=self, user_or_context="internal: update_liabilites, add missing liabilities", ) t.save() # Step 6: stray_liabilities_qs = Booking.objects.filter( member=self, credit_account=src_account, transaction__reversed_by__isnull=True, ) if membership_ranges: stray_liabilities_qs = stray_liabilities_qs.exclude(date_range_q) stray_liabilities_qs = stray_liabilities_qs.prefetch_related("transaction") for stray_liability in stray_liabilities_qs.all(): stray_liability.transaction.reverse( memo=_("Due amount outside of membership canceled"), user_or_context="internal: update_liabilites, reverse stray liabilities", )
class Meta: required_db_features = {'supports_table_check_constraints'} constraints = [ models.CheckConstraint(check=models.Q(age__gte=18), name='is_adult') ]
def search(self, query): return self.get_queryset().filter( models.Q(internal_reference__icontains=query) | models.Q(name__icontains=query) | models.Q(description__icontains=query))
def search(self, query): return self.get_queryset().filter(models.Q(name__icontains=query) | \ models.Q(created_at__icontains=query))
def object_has_region(user): """ Check if object's region is one of user regions. """ return models.Q(region__in=user.regions_ids)
class CytoScape(models.Model): ALLOWED_INITIAL_CONTENT_TYPES = models.Q(app_label='quest_manager', model='quest') | \ models.Q(app_label='badges', model='badge') | \ models.Q(app_label='courses', model='rank') name = models.CharField(max_length=250) style_set = models.ForeignKey(CytoStyleSet, null=True, on_delete=models.SET_NULL) # initial_object = models.OneToOneField(Quest) # The initial object initial_content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE, limit_choices_to=ALLOWED_INITIAL_CONTENT_TYPES) initial_object_id = models.PositiveIntegerField( help_text="The id of the object for this content type. " "You may need to look this up. E.g. If the initial type " "is a quest, then the quest's id goes here.") initial_content_object = GenericForeignKey('initial_content_type', 'initial_object_id') parent_scape = models.ForeignKey( 'self', blank=True, null=True, help_text= "The map/scape preceding this one, so it can be linked back to", on_delete=models.CASCADE) is_the_primary_scape = models.BooleanField( default=False, help_text="There can only be one primary map/scape. Making this True " "will change all other map/scapes will be set to False.") last_regeneration = models.DateTimeField(default=timezone.now) container_element_id = models.CharField( max_length=50, default="cy", help_text= "id of the html element where the graph's canvas will be placed") autobreak = models.BooleanField( default=True, help_text= "Stop the map when reaching a quest with a ~ or a badge with a *." "If this is unchecked, the map is gonna be CRAZY!") class Meta: unique_together = (('initial_content_type', 'initial_object_id'), ) verbose_name = "Quest Map" def __str__(self): return self.name def save(self, *args, **kwargs): # if this is the first scape, make it primary if CytoScape.objects.all().count() == 0: self.is_the_primary_scape = True # if setting this to primary, turn other primary to False elif self.is_the_primary_scape: try: current_primary_scape = CytoScape.objects.get( is_the_primary_scape=True) if self != current_primary_scape: current_primary_scape.is_the_chosen_one = False current_primary_scape.save() except CytoScape.DoesNotExist: pass super(CytoScape, self).save(*args, **kwargs) def get_absolute_url(self): return reverse('djcytoscape:quest_map', kwargs={'scape_id': self.id}) objects = CytoScapeManager() def json(self): elements = self.cytoelement_set.all() print(elements) json_str = "cytoscape({ \n" json_str += " container: document.getElementById('" + self.container_element_id + "'), \n" json_str += " elements: [ \n" for element in elements: json_str += element.json() json_str += " ], \n" json_str += self.style_set.get_layout_json() json_str += " style: [ \n" json_str += self.style_set.get_node_styles() json_str += self.style_set.get_edge_styles() json_str += self.style_set.get_parent_styles() json_str += self.style_set.get_classes() # json_str += self.get_selector_styles_json('.Quest', self.quest_styles) # json_str += self.get_selector_styles_json('.Badge', self.badge_styles) # json_str += self.get_selector_styles_json('.campaign', self.campaign_styles) # json_str += self.get_selector_styles_json('.hidden', self.hidden_styles) # json_str += self.get_selector_styles_json('.link', self.link_styles) # json_str += self.get_selector_styles_json('.link_hover', self.link_hover_styles) for element in elements: if element.id_styles: json_str += self.get_selector_styles_json( str(element.id), element.id_styles) json_str += " ], \n" # end style: [ json_str += self.style_set.get_init_options() json_str += "});" return json_str @staticmethod def get_selector_styles_json(selector, styles): json_str = " { \n" json_str += " selector: '#" + selector + "', \n" json_str += " style: { \n" json_str += styles json_str += " } \n" json_str += " }, \n" return json_str @staticmethod def generate_label(obj): max_len = 44 # max label length in characters post = "" pre = "" if type(obj) is Badge: pre = "Badge: " elif type(obj) is Rank: pre = "Rank: " title = pre + str(obj) # shorten the end if len(title) > max_len: title = title[:(max_len - 3)] + "..." # + title[-int(l/2-2):] if hasattr(obj, 'xp'): post = " (" + str(obj.xp) + ")" # if hasattr(obj, 'max_repeats'): # stop trying to be fancy! # if obj.max_repeats != 0: # post += ' ⟲' # json.dumps to escape special characters in the object, such as single and double quote marks. # dumps not working...just try manually title = title.replace('"', '\\"') return title + post def get_last_node_in_campaign(self, parent_node, offset=0): """ :param offset: offset from last node by this (i.e. 1 --> second last node) :param parent_node: the compound node/parent, i.e. campaign :return: the most recent node added to the campaign (added to the compound/parent node) by using the highest id """ try: # latest normally used with dates, but actually works with other fields too! # return CytoElement.objects.all_for_campaign(self, parent_node).latest('id') qs = CytoElement.objects.all_for_campaign( self, parent_node).order_by('-id') return qs[offset] except self.DoesNotExist: return None def add_node_from_object(self, obj, initial_node=False): # If node node doesn't exist already, create a new one # check for an icon if hasattr(obj, 'get_icon_url'): img_url = obj.get_icon_url() else: img_url = "none" new_node, created = CytoElement.objects.get_or_create( scape=self, group=CytoElement.NODES, selector_id=CytoElement.generate_selector_id(obj), label=self.generate_label(obj), defaults={ 'id_styles': "'background-image': '" + img_url + "'", 'classes': type(obj).__name__, }) # if this is a transition node (to a new map), add the link to href field. And add the "link" class if not initial_node and self.is_transition_node(new_node): ct = ContentType.objects.get_for_model(obj) obj = ct.get_object_for_this_type(id=obj.id) # <content_type_id>, <object_id>, <originating_scape_id> new_node.href = reverse('maps:quest_map_interlink', args=[ct.id, obj.id, self.id]) new_node.classes += " link" new_node.save() else: # add a link to the object itself new_node.href = obj.get_absolute_url() new_node.save() return new_node, created def init_temp_campaign_list(self): """ Create a new member variable `campaign_list` or clear it if it already exists """ self.campaign_list = [] def get_temp_campaign(self, campaign_id): for campaign in self.campaign_list: try: if campaign.node_id == campaign_id: return campaign except ValueError: pass return None def add_to_campaign(self, obj, node, mother_node): """ Checks if obj is in a campaign, if so, gets or creates the campaign_node (Parent/compound node) and adds is as the parent node. Also registers the node and parent with the TempCampaign (for edges later) :param mother_node: prereq node :param obj: :param node: node of the object :return: campaign_node = None if obj isn't part of a campaign, otherwise = the campaign_node (i.e. parent_node) campaign_created = False if obj is in campaign, but the campaign was created by an earlier node """ # If part of a campaign, create parent and add to "parent" node, i.e. compound node campaign_node = None campaign_created = False if hasattr(obj, 'campaign') and obj.campaign is not None: # parent_name = str(obj.campaign) campaign_node, campaign_created = CytoElement.objects.get_or_create( scape=self, group=CytoElement.NODES, label=str(obj.campaign), classes="campaign", # defaults={'attribute': value}, ) # Add parent node.data_parent = campaign_node node.save() # TempCampaign utility if campaign_created: self.campaign_list.append(TempCampaign(campaign_node.id)) temp_campaign = self.get_temp_campaign(campaign_node.id) # temp_campaign.add_child(node.id) # TODO: Nodes might be present multiple times through different mothers? check and combine temp_campaign.add_node(node.id, mother_node.id) # temp_campaign.add_prereq(mother_node.id) return campaign_node, campaign_created def fix_nonsequential_campaign_edges(self): """ cyto dagre layout doesn't support compound/parent nodes, so for non-sequential/non-directed campaigns (i.e. all quests are available concurrently) we need to: 1. add invisible edges joining the quests 2. remove edges between common prereqs and quests 3. remove edges between quests and common reliants 4. add edges between common prereqs and campaign/compound/parent node 5. add edges between campaign/compound/parent node and common reliants 6. add invisible edge (for structure) from prereqs to first node 7. add invisible edge (for structure) from last node to reliants """ for campaign in self.campaign_list: common_prereq_ids = campaign.get_common_prereq_node_ids() if common_prereq_ids: # then non-sequential campaign # 1. add invisible edges joining the quests for current_node in campaign.nodes: next_node = campaign.get_next_node(current_node) if next_node: CytoElement.objects.get_or_create( scape=self, group=CytoElement.EDGES, data_source_id=current_node.id, data_target_id=next_node.id, defaults={ 'classes': 'hidden', }, ) first_node = campaign.get_first_node() for prereq_node_id in common_prereq_ids: # 2. remove edges between common prereqs and quests for quest_node in campaign.nodes: # we already know all quests have this prereq node in common, so the edges should all exist # unless quest has internal prereq... if prereq_node_id in quest_node.prereq_node_ids: edge_node = get_object_or_404( CytoElement, data_source_id=prereq_node_id, data_target_id=quest_node.id) edge_node.delete() # 4. add edges between common prereqs and campaign/compound/parent node CytoElement.objects.get_or_create( scape=self, group=CytoElement.EDGES, data_source_id=prereq_node_id, data_target_id=campaign.node_id, # defaults={'attribute': value}, ) # 6. add invisible edge (for structure) from prereqs to first node CytoElement.objects.get_or_create( scape=self, group=CytoElement.EDGES, data_source_id=prereq_node_id, data_target_id=first_node.id, defaults={ 'classes': 'hidden', }) last_node = campaign.get_last_node() reliant_node_ids = campaign.get_common_reliant_node_ids() if reliant_node_ids: for reliant_node_id in reliant_node_ids: # 3 remove edges between quests and common reliants for quest_node in campaign.nodes: # we already know all quests have this reliant node in common, so the edges should all exist # unless it has an internal reliant... if reliant_node_id in quest_node.reliant_node_ids: edge_node = get_object_or_404( CytoElement, data_source_id=quest_node.id, data_target_id=reliant_node_id) edge_node.delete() # 5. add edges between campaign/compound/parent node and common reliants CytoElement.objects.get_or_create( scape=self, group=CytoElement.EDGES, data_source_id=campaign.node_id, data_target_id=reliant_node_id, # defaults={'attribute': value}, ) # 7. add invisible edge (for structure) from last node to reliants CytoElement.objects.get_or_create( scape=self, group=CytoElement.EDGES, data_source_id=last_node.id, data_target_id=reliant_node_id, defaults={ 'classes': 'hidden', } # defaults={'attribute': value}, ) def add_reliant(self, current_obj, mother_node): reliant_objects = current_obj.get_reliant_objects() for obj in reliant_objects: # mother_node # > obj (reliant node 1) # > obj (reliant node 2) # > ... # create the new reliant node if it doesn't already exist new_node, created = self.add_node_from_object(obj) # if mother node is in a campaign/parent, add new_node as a reliant in the temp_campaign if mother_node.data_parent: temp_campaign = self.get_temp_campaign( mother_node.data_parent.id) temp_campaign.add_reliant(mother_node.id, new_node.id) # add new_node to a campaign/compound/parent, if required self.add_to_campaign(obj, new_node, mother_node) # TODO: should add number of times prereq is required, similar to repeat edges below CytoElement.objects.get_or_create( scape=self, group=CytoElement.EDGES, data_source=mother_node, data_target=new_node, # defaults={'attribute': value}, ) # If repeatable, add circular edge # TODO: cool idea, but currently big edge gets in the way, need a tight small one. # if hasattr(obj, 'max_repeats'): # if obj.max_repeats != 0: # if obj.max_repeats < 0: # label = '∞' # else: # label = 'x ' + str(obj.max_repeats) # repeat_edge = CytoElement( # scape=self, group=CytoElement.EDGES, # data_source=new_node, # data_target=new_node, # label=label, # ) # repeat_edge.save() # recursive, continue adding if this is a new node, and not a closing node if created and not self.is_transition_node(new_node): self.add_reliant(obj, new_node) def is_transition_node(self, node): """ :return: True if node.label begins with the tilde '~' or contains an astrix '*' """ if self.autobreak: return node.label[0] == "~" or "*" in node.label else: return False @staticmethod def generate_map(initial_object, name, parent_scape=None, container_element_id="cy", autobreak=True): if parent_scape: style_set = parent_scape.style_set else: style_set, created = CytoStyleSet.objects.get_or_create( name=CytoStyleSet.DEFAULT_NAME) scape = CytoScape( name=name, initial_content_object=initial_object, parent_scape=parent_scape, container_element_id=container_element_id, style_set=style_set, autobreak=autobreak, ) scape.save() scape.calculate_nodes() return scape def calculate_nodes(self): # Create the starting node from the initial quest mother_node, created = self.add_node_from_object( self.initial_content_object, initial_node=True) # Temp campaign list used to track funky edges required for compound nodes to display properly with dagre self.init_temp_campaign_list() # Add nodes reliant on the mother_node, this is recursive and will generate all nodes until endpoints reached # Endpoints... not sure yet, but probably quests starting with '~' tilde character, or add a new field? self.add_reliant(self.initial_content_object, mother_node) # Add those funky edges for proper display of compound (parent) nodes in cyto dagre layout self.fix_nonsequential_campaign_edges() self.last_regeneration = timezone.now() self.save() def regenerate(self): # Delete existing nodes CytoElement.objects.all_for_scape(self).delete() self.calculate_nodes()
def has_long_title(user): return models.Q(title__regex=r'.{10}.*')
def has_region(user): """ Check if user has rights to region. """ return models.Q(id__in=user.regions_ids)
def is_collabolator(user): return models.Q(collaborators=user)
def is_author(user): return models.Q(author=user)
def search(self, query): return self.get_queryset().filter(models.Q(name_icontains=query))
def indicator_get_defined_targets_filter(): """returns a set of filters that filter out indicators that do not have all defined targets this version is used for an indicator_set that has been annotated with program_months (see get_program_months_annotation)""" filters = [] # LOP indicators require a defined lop_target: filters.append(models.Q(target_frequency=Indicator.LOP) & models.Q(lop_target__isnull=False)) # MID_END indicators require 2 defined targets (mid and end): filters.append(models.Q(target_frequency=Indicator.MID_END) & models.Q(defined_targets__isnull=False) & models.Q(defined_targets__gte=2)) # EVENT indicators require at least 1 defined target: filters.append(models.Q(target_frequency=Indicator.EVENT) & models.Q(defined_targets__isnull=False) & models.Q(defined_targets__gte=1)) # TIME_AWARE indicators need a number of indicators defined by the annotation on the program: # note the program_months field annotation is required for this annotation to succeed for frequency, month_count in TIME_AWARE_FREQUENCIES: period_count_filter = models.Q(defined_targets__gte=models.F('program_months') / month_count) filters.append(models.Q(target_frequency=frequency) & models.Q(defined_targets__isnull=False) & period_count_filter) combined_filter = filters.pop() for filt in filters: combined_filter |= filt return combined_filter
def file_size(self): queryset = self.values.filter( snapshot=None).exclude(models.Q(file='') | models.Q(file=None)) return sum([value.file.size for value in queryset])
def indicator_lop_annotations(): """generates annotations for LOP target and actual data takes into account cumulative/noncumulative and number/percent""" # we only want targets that are either time naive or for completed periods: data_subquery = CollectedData.objects.filter( periodic_target_id=models.OuterRef('pk') ).order_by().values('id') indicator_targets = PeriodicTarget.objects.annotate( data_count=models.Subquery( data_subquery.annotate(total=models.Count('achieved')).values('total')[:1], output_field=models.IntegerField() ) ).filter( models.Q(indicator=models.OuterRef('pk')), ( models.Q(indicator__target_frequency__in=[Indicator.LOP, Indicator.MID_END, Indicator.EVENT]) & models.Q(data_count__gt=0) ) | models.Q(end_date__lte=models.functions.Now()) ) indicator_data = CollectedData.objects.filter( models.Q(indicator=models.OuterRef('pk')) ) lop_target_sum = models.Case( models.When( # the only target for a LOP indicator is the "lop_target" value on the indicator itself: target_frequency=Indicator.LOP, then=models.ExpressionWrapper( models.F('lop_target'), output_field=models.FloatField() ) ), models.When( # cumulative indicator means sort and take the most recent: is_cumulative=True, then=models.Case( # midline/endling and event indicators sort on customsort (with fallbacks) models.When( target_frequency__in=[Indicator.MID_END, Indicator.EVENT], then=models.Subquery( indicator_targets.order_by( '-customsort', '-end_date', '-create_date' ).values('target')[:1], output_field=models.FloatField() ) ), # not midline/endline means it's time-aware, sort on end_date: default=models.Subquery( indicator_targets.order_by('-end_date').values('target')[:1], output_field=models.FloatField()) ) ), default=models.Subquery( indicator_targets.order_by().values( 'indicator_id' ).annotate( total=models.Sum('target') ).values('total'), output_field=models.FloatField() ) ) # lop_actual_sum = sum of all the data collected: lop_actual_sum = models.Case( models.When( unit_of_measure_type=Indicator.PERCENTAGE, then=Round( models.Subquery( indicator_data.order_by( '-date_collected' ).values('achieved')[:1], output_field=models.FloatField() ) ) ), default=models.Subquery( indicator_data.order_by().values( 'indicator_id' ).annotate( total=models.Sum('achieved') ).values('total'), output_field=models.FloatField() ) ) return { 'lop_target_sum': lop_target_sum, 'lop_actual_sum': lop_actual_sum, }
def get_query_set(self): use_distinct = False qs = self.root_query_set lookup_params = self.params.copy() # a dictionary of the query string for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR): if i in lookup_params: del lookup_params[i] for key, value in lookup_params.items(): if not isinstance(key, str): # 'key' will be used as a keyword argument later, so Python # requires it to be a string. del lookup_params[key] lookup_params[smart_str(key)] = value if not use_distinct: # Check if it's a relationship that might return more than one # instance field_name = key.split('__', 1)[0] try: f = self.lookup_opts.get_field_by_name(field_name)[0] except models.FieldDoesNotExist: raise IncorrectLookupParameters use_distinct = field_needs_distinct(f) # if key ends with __in, split parameter into separate values if key.endswith('__in'): value = value.split(',') lookup_params[key] = value # if key ends with __isnull, special case '' and false if key.endswith('__isnull'): if value.lower() in ('', 'false'): value = False else: value = True lookup_params[key] = value if not self.model_admin.lookup_allowed(key, value): raise SuspiciousOperation("Filtering by %s not allowed" % key) # Apply lookup parameters from the query string. try: qs = qs.filter(**lookup_params) # Naked except! Because we don't have any other way of validating "params". # They might be invalid if the keyword arguments are incorrect, or if the # values are not in the correct type, so we might get FieldError, ValueError, # ValicationError, or ? from a custom field that raises yet something else # when handed impossible data. except: raise IncorrectLookupParameters # Use select_related() if one of the list_display options is a field # with a relationship and the provided queryset doesn't already have # select_related defined. if not qs.query.select_related: if self.list_select_related: qs = qs.select_related() else: for field_name in self.list_display: try: f = self.lookup_opts.get_field(field_name) except models.FieldDoesNotExist: pass else: if isinstance(f.rel, models.ManyToOneRel): qs = qs.select_related() break # Set ordering. if self.order_field: qs = qs.order_by( '%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field)) # Apply keyword searches. def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name if self.search_fields and self.query: orm_lookups = [ construct_search(str(search_field)) for search_field in self.search_fields ] for bit in self.query.split(): or_queries = [ models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups ] qs = qs.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: field_name = search_spec.split('__', 1)[0] f = self.lookup_opts.get_field_by_name(field_name)[0] if field_needs_distinct(f): use_distinct = True break if use_distinct: return qs.distinct() else: return qs
def today_callable_q(): return models.Q(last_action__gte=datetime.datetime.today())
def hugs_come_and_gone(self): return Hug.objects.filter(models.Q(source=self.user) | models.Q(target=self.user)).order_by('-timestamp')
def mine(self, user): return self.get_queryset().filter( models.Q(sender=user) | models.Q(receiver=user))
def _get_related_jobs(self): return UnifiedJob.objects.non_polymorphic().filter( models.Q(Job___project=self) | models.Q(ProjectUpdate___project=self))
class Meta: constraints = [ models.CheckConstraint(check=models.Q(age__gte=18), name='is_adult') ]
class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name='Company', fields=[ ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, verbose_name='created')), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, verbose_name='modified')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('name', models.TextField()), ('location', models.TextField()), ('slogan', models.TextField(blank=True, default='')), ('logo_url', models.URLField(blank=True, default='')), ], options={ 'abstract': False, }, ), migrations.CreateModel( name='CompanyUserPermissions', fields=[ ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, verbose_name='created')), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, verbose_name='modified')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('is_admin', models.BooleanField(default=False)), ('can_edit', models.BooleanField(default=False)), ('can_create_jobs', models.BooleanField(default=False)), ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='company_user_permissions', to='api.company')), ], options={ 'abstract': False, }, ), migrations.CreateModel( name='User', fields=[ ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, verbose_name='created')), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, verbose_name='modified')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('name', models.TextField()), ('email', models.EmailField(max_length=254)), ('auth0_id', models.TextField()), ('linkedin_id', models.TextField()), ('github_username', models.TextField(blank=True, default='')), ('is_developer', models.BooleanField(default=False)), ('location', models.TextField()), ('avatar_url', models.URLField(blank=True, default='')), ('languages', django.contrib.postgres.fields.ArrayField( base_field=models.TextField(), size=None)), ('skills', django.contrib.postgres.fields.ArrayField( base_field=models.TextField(), size=None)), ('companies', models.ManyToManyField(through='api.CompanyUserPermissions', to='api.Company')), ], ), migrations.CreateModel( name='Job', fields=[ ('created', model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, verbose_name='created')), ('modified', model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, verbose_name='modified')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('title', models.TextField()), ('location', models.TextField()), ('description', models.TextField()), ('skills', django.contrib.postgres.fields.ArrayField( base_field=models.TextField(), size=None)), ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='api.company')), ], options={ 'abstract': False, }, ), migrations.AddField( model_name='companyuserpermissions', name='user', field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name='company_user_permissions', to='api.user'), ), migrations.AddField( model_name='company', name='users', field=models.ManyToManyField(through='api.CompanyUserPermissions', to='api.User'), ), migrations.AddConstraint( model_name='user', constraint=models.CheckConstraint( check=models.Q( ('is_developer', False), models.Q(('is_developer', True), models.Q(_negated=True, github_username__exact='')), _connector='OR'), name='required_developer_fields'), ), migrations.AddConstraint( model_name='user', constraint=models.UniqueConstraint(condition=models.Q( _negated=True, github_username__exact=''), fields=('github_username', ), name='unique_github_username'), ), ]
class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Game', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('date', models.DateTimeField(default=django.utils.timezone.now)), ('game_map', models.CharField(choices=[('Tharsis', 'Tharsis'), ('Elysium', 'Elysium'), ('Hellas', 'Hellas')], default='Tharsis', max_length=16)), ('draft_variant', models.BooleanField(default=True)), ('prelude', models.BooleanField(default=False)), ('venus_next', models.BooleanField(default=False)), ('colonies', models.BooleanField(default=False)), ], ), migrations.CreateModel( name='Player', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('nickname', models.CharField(max_length=32, unique=True)), ('motto', models.CharField(blank=True, max_length=128, null=True)), ('user', models.OneToOneField( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='PlayerScore', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('corporation', models.CharField(choices=[ ('Aphrodite', 'Aphrodite'), ('Aridor', 'Aridor'), ('Arklight', 'Arklight'), ('Celestic', 'Celestic'), ('Cheung Shing Mars', 'Cheung Shing Mars'), ('Credicor', 'Credicor'), ('Ecoline', 'Ecoline'), ('Helion', 'Helion'), ('Interplanetary Cinematics', 'Interplanetary Cinematics'), ('Inventrix', 'Inventrix'), ('Manutech', 'Manutech'), ('Mining Guild', 'Mining Guild'), ('Morning Star Inc.', 'Morning Star Inc.'), ('Phobolog', 'Phobolog'), ('Point Luna', 'Point Luna'), ('Polyphemos', 'Polyphemos'), ('Poseidon', 'Poseidon'), ('Robinson Industries', 'Robinson Industries'), ('Saturn Systems', 'Saturn Systems'), ('Storm Craft Incorporated', 'Storm Craft Incorporated'), ('Terractor', 'Terractor'), ('Tharsis Republic', 'Tharsis Republic'), ('Thorgate', 'Thorgate'), ('United Nations Mars Initiative', 'United Nations Mars Initiative'), ('Valley Trust', 'Valley Trust'), ('Viron', 'Viron'), ('Vitor', 'Vitor') ], max_length=64)), ('terraform_rating', models.PositiveSmallIntegerField(default=20)), ('milestones', models.PositiveSmallIntegerField(default=0)), ('awards', models.PositiveSmallIntegerField(default=0)), ('greeneries', models.PositiveSmallIntegerField(default=0)), ('cities', models.PositiveSmallIntegerField(default=0)), ('event_cards', models.SmallIntegerField(default=0)), ('automated_cards', models.SmallIntegerField(default=0)), ('active_cards', models.SmallIntegerField(default=0)), ('resources', models.SmallIntegerField(default=0)), ('game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scores', to='mars_api.game')), ('player', models.ForeignKey( null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='scores', to='mars_api.player')), ], options={ 'default_related_name': 'scores', }, ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Hellas', 'Elysium', 'Tharsis'} maps are allowed." ), ), migrations.AddConstraint( model_name='playerscore', constraint=models.UniqueConstraint( fields=('player', 'game'), name='one_score_per_player_per_game'), ), migrations.AddConstraint( model_name='playerscore', constraint=models.UniqueConstraint( fields=('corporation', 'game'), name='only_unique_corporations_per_game'), ), migrations.AddConstraint( model_name='playerscore', constraint=models.CheckConstraint( check=models.Q( corporation__in={ 'Celestic', 'Thorgate', 'Cheung Shing Mars', 'Poseidon', 'Storm Craft Incorporated', 'Helion', 'Inventrix', 'Aridor', 'Valley Trust', 'Viron', 'Robinson Industries', 'United Nations Mars Initiative', 'Vitor', 'Point Luna', 'Arklight', 'Aphrodite', 'Polyphemos', 'Phobolog', 'Manutech', 'Terractor', 'Ecoline', 'Tharsis Republic', 'Morning Star Inc.', 'Mining Guild', 'Saturn Systems', 'Interplanetary Cinematics', 'Credicor' }), name='Only defined corporations are allowed.'), ), migrations.RemoveConstraint( model_name='game', name="Only {'Hellas', 'Elysium', 'Tharsis'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Elysium', 'Hellas', 'Tharsis'} maps are allowed." ), ), migrations.RemoveConstraint( model_name='game', name="Only {'Elysium', 'Hellas', 'Tharsis'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Hellas', 'Elysium', 'Tharsis'} maps are allowed." ), ), migrations.RemoveConstraint( model_name='game', name="Only {'Hellas', 'Elysium', 'Tharsis'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Elysium', 'Hellas', 'Tharsis'} maps are allowed." ), ), migrations.RemoveConstraint( model_name='game', name="Only {'Elysium', 'Hellas', 'Tharsis'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Hellas', 'Elysium', 'Tharsis'} maps are allowed." ), ), migrations.RemoveConstraint( model_name='game', name="Only {'Hellas', 'Elysium', 'Tharsis'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Elysium', 'Hellas', 'Tharsis'} maps are allowed." ), ), migrations.RemoveConstraint( model_name='game', name="Only {'Elysium', 'Hellas', 'Tharsis'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name="Only {'Elysium', 'Tharsis', 'Hellas'} maps are allowed." ), ), migrations.RemoveConstraint( model_name='game', name="Only {'Elysium', 'Tharsis', 'Hellas'} maps are allowed.", ), migrations.AddConstraint( model_name='game', constraint=models.CheckConstraint( check=models.Q(game_map__in={'Hellas', 'Tharsis', 'Elysium'}), name='Only defined maps are allowed.'), ), migrations.RemoveConstraint( model_name='playerscore', name='Only defined corporations are allowed.', ), migrations.AddField( model_name='game', name='number_of_generations', field=models.PositiveSmallIntegerField(default=10), ), migrations.AlterField( model_name='playerscore', name='corporation', field=models.CharField(choices=[ ('Aphrodite', 'Aphrodite'), ('Aridor', 'Aridor'), ('Arklight', 'Arklight'), ('Celestic', 'Celestic'), ('Cheung Shing Mars', 'Cheung Shing Mars'), ('Credicor', 'Credicor'), ('Ecoline', 'Ecoline'), ('Helion', 'Helion'), ('Interplanetary Cinematics', 'Interplanetary Cinematics'), ('Inventrix', 'Inventrix'), ('N/A', 'N/A'), ('Manutech', 'Manutech'), ('Mining Guild', 'Mining Guild'), ('Morning Star Inc.', 'Morning Star Inc.'), ('Phobolog', 'Phobolog'), ('Point Luna', 'Point Luna'), ('Polyphemos', 'Polyphemos'), ('Poseidon', 'Poseidon'), ('Robinson Industries', 'Robinson Industries'), ('Saturn Systems', 'Saturn Systems'), ('Storm Craft Incorporated', 'Storm Craft Incorporated'), ('Teractor', 'Teractor'), ('Tharsis Republic', 'Tharsis Republic'), ('Thorgate', 'Thorgate'), ('United Nations Mars Initiative', 'United Nations Mars Initiative'), ('Valley Trust', 'Valley Trust'), ('Viron', 'Viron'), ('Vitor', 'Vitor') ], max_length=64), ), migrations.AddConstraint( model_name='playerscore', constraint=models.CheckConstraint( check=models.Q( corporation__in={ 'Celestic', 'Thorgate', 'Poseidon', 'Cheung Shing Mars', 'Storm Craft Incorporated', 'Helion', 'Inventrix', 'Aridor', 'Valley Trust', 'Viron', 'Robinson Industries', 'Teractor', 'United Nations Mars Initiative', 'Vitor', 'Point Luna', 'Arklight', 'Aphrodite', 'Phobolog', 'Polyphemos', 'Manutech', 'N/A', 'Ecoline', 'Tharsis Republic', 'Morning Star Inc.', 'Mining Guild', 'Saturn Systems', 'Interplanetary Cinematics', 'Credicor' }), name='Only defined corporations are allowed.'), ), migrations.RemoveConstraint( model_name='playerscore', name='only_unique_corporations_per_game', ), migrations.AddConstraint( model_name='playerscore', constraint=models.UniqueConstraint( condition=models.Q(_negated=True, corporation__startswith='N/A'), fields=('corporation', 'game'), name='only_unique_corporations_per_game'), ), migrations.RemoveConstraint( model_name='playerscore', name='only_unique_corporations_per_game', ), migrations.AddConstraint( model_name='playerscore', constraint=models.UniqueConstraint( fields=('corporation', 'game'), name='only_unique_corporations_per_game'), ), migrations.RemoveConstraint( model_name='playerscore', name='only_unique_corporations_per_game', ), migrations.AddConstraint( model_name='playerscore', constraint=models.UniqueConstraint( condition=models.Q(_negated=True, corporation__startswith='N/A'), fields=('corporation', 'game'), name='only_unique_corporations_per_game'), ), ]
def between(self, user1, user2): return self.filter( models.Q(user1=user1, user2=user2) | models.Q(user1=user2, user2=user1))
def _delete(self, destroy=True, cascade=True, systemdataset=None): """ Some places reference a path which will not cascade delete We need to manually find all paths within this volume mount point """ from freenasUI.services.models import iSCSITargetExtent # If we are using this volume to store collectd data # the service needs to be restarted if systemdataset and systemdataset.sys_rrd_usedataset: reload_collectd = True else: reload_collectd = False # TODO: This is ugly. svcs = ('cifs', 'afp', 'nfs', 'iscsitarget', 'jails', 'collectd') reloads = (False, False, False, False, False, reload_collectd) n = notifier() if cascade: reloads = map(sum, zip(reloads, self.delete_attachments())) zvols = n.list_zfs_vols(self.vol_name) for zvol in zvols: qs = iSCSITargetExtent.objects.filter( iscsi_target_extent_path='zvol/' + zvol, iscsi_target_extent_type='ZVOL') if qs.exists(): if destroy: notifier().destroy_zfs_vol(zvol) qs.delete() reloads = map( sum, zip(reloads, (False, False, False, True, False, reload_collectd))) else: attachments = self.has_attachments() reloads = map( sum, zip(reloads, [len(attachments[svc]) for svc in svcs])) # Delete scheduled snapshots for this volume Task.objects.filter( models.Q(task_filesystem=self.vol_name) | models.Q(task_filesystem__startswith="%s/" % self.vol_name)).delete() for (svc, dirty) in zip(svcs, reloads): if dirty: n.stop(svc) n.detach_volume_swaps(self) # Ghosts volumes, does not exists anymore but is in database ghost = False try: status = n.get_volume_status(self.vol_name, self.vol_fstype) ghost = status == 'UNKNOWN' except: ghost = True if ghost: pass elif destroy: n.destroy("volume", self) else: n.volume_detach(self) return (svcs, reloads)
def for_user(self, user): return self.filter(models.Q(user1=user) | models.Q(user2=user))
def queryset(self, request): qs = super(PostAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter( models.Q(user=request.user) | models.Q(author=request.user))
def create(self, validated_data): """ Create an Order and charge the user. """ user = self.context['request'].user # if validated_data.get('target_user', None): # if user.is_staff: # user = validated_data.pop('target_user') # else: # raise serializers.ValidationError({ # 'non_field_errors': [_( # "You cannot create an order for another user without " # "admin rights." # )] # }) orderlines_data = validated_data.pop('order_lines') payment_token = validated_data.pop('payment_token', None) single_use_token = validated_data.pop('single_use_token', None) # Temporary IDs until the external profile is created. validated_data['authorization_id'] = "0" validated_data['settlement_id'] = "0" validated_data['reference_number'] = "0" validated_data['transaction_date'] = timezone.now() validated_data['user'] = user profile = PaymentProfile.objects.filter(owner=user).first() retreat_reservations = list() if single_use_token and not profile: # Create external profile try: create_profile_response = create_external_payment_profile(user) except PaymentAPIError as err: raise serializers.ValidationError({'non_field_errors': [err]}) # Create local profile profile = PaymentProfile.objects.create( name="Paysafe", owner=user, external_api_id=create_profile_response.json()['id'], external_api_url='{0}{1}'.format( create_profile_response.url, create_profile_response.json()['id'])) with transaction.atomic(): coupon = validated_data.pop('coupon', None) order = Order.objects.create(**validated_data) charge_response = None discount_amount = 0 for orderline_data in orderlines_data: OrderLine.objects.create(order=order, **orderline_data) if coupon: coupon_info = validate_coupon_for_order(coupon, order) if coupon_info['valid_use']: coupon_user = CouponUser.objects.get( user=user, coupon=coupon, ) coupon_user.uses = coupon_user.uses + 1 coupon_user.save() discount_amount = coupon_info['value'] orderline_cost = coupon_info['orderline'].cost coupon_info['orderline'].cost = (orderline_cost - discount_amount) coupon_info['orderline'].coupon = coupon coupon_info['orderline'].coupon_real_value = coupon_info[ 'value'] coupon_info['orderline'].save() else: raise serializers.ValidationError(coupon_info['error']) amount = order.total_cost tax = amount * Decimal(repr(TAX_RATE)) tax = tax.quantize(Decimal('0.01')) amount *= Decimal(repr(TAX_RATE + 1)) amount = round(amount * 100, 2) membership_orderlines = order.order_lines.filter( content_type__model="membership") package_orderlines = order.order_lines.filter( content_type__model="package") reservation_orderlines = order.order_lines.filter( content_type__model="timeslot") retreat_orderlines = order.order_lines.filter( content_type__model="retreat") need_transaction = False if membership_orderlines: need_transaction = True today = timezone.now().date() if user.membership and user.membership_end > today: raise serializers.ValidationError({ 'non_field_errors': [_("You already have an active membership.")] }) user.membership = membership_orderlines[0].content_object user.membership_end = (timezone.now().date() + user.membership.duration) user.save() membership_coupons = MembershipCoupon.objects.filter( membership__pk=membership_orderlines[0].content_object.pk) for membership_coupon in membership_coupons: coupon = Coupon.objects.create( value=membership_coupon.value, percent_off=membership_coupon.percent_off, max_use=membership_coupon.max_use, max_use_per_user=membership_coupon.max_use_per_user, details=membership_coupon.details, start_time=timezone.now(), end_time=timezone.now() + membership_orderlines[0].content_object.duration, owner=user) coupon.applicable_retreats.set( membership_coupon.applicable_retreats.all()) coupon.applicable_timeslots.set( membership_coupon.applicable_timeslots.all()) coupon.applicable_packages.set( membership_coupon.applicable_packages.all()) coupon.applicable_memberships.set( membership_coupon.applicable_memberships.all()) coupon.applicable_product_types.set( membership_coupon.applicable_product_types.all()) coupon.generate_code() coupon.save() if package_orderlines: need_transaction = True for package_orderline in package_orderlines: user.tickets += ( package_orderline.content_object.reservations * package_orderline.quantity) user.save() if reservation_orderlines: for reservation_orderline in reservation_orderlines: timeslot = reservation_orderline.content_object reservations = timeslot.reservations.filter(is_active=True) reserved = reservations.count() if timeslot.billing_price > user.tickets: raise serializers.ValidationError({ 'non_field_errors': [ _("You don't have enough tickets to make this " "reservation.") ] }) if reservations.filter(user=user): raise serializers.ValidationError({ 'non_field_errors': [ _("You already are registered to this timeslot: " "{0}.".format(str(timeslot))) ] }) if (timeslot.period.workplace and timeslot.period.workplace.seats - reserved > 0): Reservation.objects.create(user=user, timeslot=timeslot, is_active=True) # Decrement user tickets for each reservation. # OrderLine's quantity and TimeSlot's price will be # used in the future if we want to allow multiple # reservations of the same timeslot. user.tickets -= 1 user.save() else: raise serializers.ValidationError({ 'non_field_errors': [ _("There are no places left in the requested " "timeslot.") ] }) if retreat_orderlines: need_transaction = True if not (user.phone and user.city): raise serializers.ValidationError({ 'non_field_errors': [ _("Incomplete user profile. 'phone' and 'city' " "field must be filled in the user profile to book " "a retreat.") ] }) for retreat_orderline in retreat_orderlines: retreat = retreat_orderline.content_object user_waiting = retreat.wait_queue.filter(user=user) reservations = retreat.reservations.filter(is_active=True) reserved = reservations.count() if reservations.filter(user=user): raise serializers.ValidationError({ 'non_field_errors': [ _("You already are registered to this " "retreat: {0}.".format(str(retreat))) ] }) if (((retreat.seats - retreat.total_reservations - retreat.reserved_seats) > 0) or (retreat.reserved_seats and WaitQueueNotification.objects.filter( user=user, retreat=retreat))): retreat_reservations.append( RetreatReservation.objects.create( user=user, retreat=retreat, order_line=retreat_orderline, is_active=True)) # Decrement reserved_seats if > 0 if retreat.reserved_seats: retreat.reserved_seats = (retreat.reserved_seats - 1) retreat.save() else: raise serializers.ValidationError({ 'non_field_errors': [ _("There are no places left in the requested " "retreat.") ] }) if user_waiting: user_waiting.delete() if need_transaction and payment_token and int(amount): # Charge the order with the external payment API try: charge_response = charge_payment(int(round(amount)), payment_token, str(order.id)) except PaymentAPIError as err: raise serializers.ValidationError( {'non_field_errors': [err]}) elif need_transaction and single_use_token and int(amount): # Add card to the external profile & charge user try: card_create_response = create_external_card( profile.external_api_id, single_use_token) charge_response = charge_payment( int(round(amount)), card_create_response.json()['paymentToken'], str(order.id)) except PaymentAPIError as err: raise serializers.ValidationError( {'non_field_errors': [err]}) elif (membership_orderlines or package_orderlines or retreat_orderlines) and int(amount): raise serializers.ValidationError({ 'non_field_errors': [ _("A payment_token or single_use_token is required to " "create an order.") ] }) if need_transaction: if charge_response: charge_res_content = charge_response.json() order.authorization_id = charge_res_content['id'] order.settlement_id = charge_res_content['settlements'][0][ 'id'] order.reference_number = charge_res_content[ 'merchantRefNum'] else: charge_res_content = { 'card': { 'lastDigits': None, 'type': "NONE" } } order.authorization_id = 0 order.settlement_id = 0 order.reference_number = "charge-" + str(uuid.uuid4()) order.save() if need_transaction: # Send order email orderlines = order.order_lines.filter( models.Q(content_type__model='membership') | models.Q(content_type__model='package') | models.Q(content_type__model='retreat')) # Here, the 'details' key is used to provide details of the # item to the email template. # As of now, only 'retreat' objects have the 'email_content' # key that is used here. There is surely a better way to # to handle that logic that will be more generic. items = [ { 'price': orderline.content_object.price, 'name': "{0}: {1}".format(str(orderline.content_type), orderline.content_object.name), # Removed details section because it was only used # for retreats. Retreats instead have another # unique email containing details of the event. # 'details': # orderline.content_object.email_content if hasattr( # orderline.content_object, 'email_content' # ) else "" } for orderline in orderlines ] # Send order confirmation email merge_data = { 'STATUS': "APPROUVÉE", 'CARD_NUMBER': charge_res_content['card']['lastDigits'], 'CARD_TYPE': PAYSAFE_CARD_TYPE[charge_res_content['card']['type']], 'DATETIME': timezone.localtime().strftime("%x %X"), 'ORDER_ID': order.id, 'CUSTOMER_NAME': user.first_name + " " + user.last_name, 'CUSTOMER_EMAIL': user.email, 'CUSTOMER_NUMBER': user.id, 'AUTHORIZATION': order.authorization_id, 'TYPE': "Achat", 'ITEM_LIST': items, 'TAX': tax, 'DISCOUNT': discount_amount, 'COUPON': coupon, 'SUBTOTAL': round(amount / 100 - tax, 2), 'COST': round(amount / 100, 2), } plain_msg = render_to_string("invoice.txt", merge_data) msg_html = render_to_string("invoice.html", merge_data) send_mail( "Confirmation d'achat", plain_msg, settings.DEFAULT_FROM_EMAIL, [order.user.email], html_message=msg_html, ) # Send retreat informations emails for retreat_reservation in retreat_reservations: # Send info email merge_data = { 'RETREAT': retreat_reservation.retreat, 'USER': user, } plain_msg = render_to_string("retreat_info.txt", merge_data) msg_html = render_to_string("retreat_info.html", merge_data) send_mail( "Confirmation d'inscription à la retraite", plain_msg, settings.DEFAULT_FROM_EMAIL, [retreat_reservation.user.email], html_message=msg_html, ) return order