Example #1
0
    def annotate_queryset(queryset):
        """
        Add some extra annotations to the queryset,
        performing database queries as efficiently as possible,
        to reduce database trips.
        """

        # Annotate with the total 'in stock' quantity
        queryset = queryset.annotate(
            in_stock=Coalesce(
                SubquerySum('stock_items__quantity', filter=StockItem.IN_STOCK_FILTER),
                Decimal(0)
            ),
        )

        # Annotate with the total number of stock items
        queryset = queryset.annotate(
            stock_item_count=SubqueryCount('stock_items')
        )

        # Filter to limit builds to "active"
        build_filter = Q(
            status__in=BuildStatus.ACTIVE_CODES
        )

        # Annotate with the total 'building' quantity
        queryset = queryset.annotate(
            building=Coalesce(
                SubquerySum('builds__quantity', filter=build_filter),
                Decimal(0),
            )
        )
        
        # Filter to limit orders to "open"
        order_filter = Q(
            order__status__in=PurchaseOrderStatus.OPEN
        )

        # Annotate with the total 'on order' quantity
        queryset = queryset.annotate(
            ordering=Coalesce(
                SubquerySum('supplier_parts__purchase_order_line_items__quantity', filter=order_filter),
                Decimal(0),
            ) - Coalesce(
                SubquerySum('supplier_parts__purchase_order_line_items__received', filter=order_filter),
                Decimal(0),
            )
        )

        # Annotate with the number of 'suppliers'
        queryset = queryset.annotate(
            suppliers=Coalesce(
                SubqueryCount('supplier_parts'),
                Decimal(0),
            ),
        )
        
        return queryset
Example #2
0
    def annotate_queryset(queryset):
        """
        Add some extra annotations to the queryset,
        performing database queries as efficiently as possible,
        to reduce database trips.
        """

        # TODO: Update the "in_stock" annotation to include stock for variants of the part
        # Ref: https://github.com/inventree/InvenTree/issues/2240

        # Annotate with the total 'in stock' quantity
        queryset = queryset.annotate(in_stock=Coalesce(
            SubquerySum('stock_items__quantity',
                        filter=StockItem.IN_STOCK_FILTER),
            Decimal(0),
            output_field=models.DecimalField(),
        ), )

        # Annotate with the total number of stock items
        queryset = queryset.annotate(
            stock_item_count=SubqueryCount('stock_items'))

        # Filter to limit builds to "active"
        build_filter = Q(status__in=BuildStatus.ACTIVE_CODES)

        # Annotate with the total 'building' quantity
        queryset = queryset.annotate(building=Coalesce(
            SubquerySum('builds__quantity', filter=build_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))

        # Filter to limit orders to "open"
        order_filter = Q(order__status__in=PurchaseOrderStatus.OPEN)

        # Annotate with the total 'on order' quantity
        queryset = queryset.annotate(ordering=Coalesce(
            SubquerySum('supplier_parts__purchase_order_line_items__quantity',
                        filter=order_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ) - Coalesce(
            SubquerySum('supplier_parts__purchase_order_line_items__received',
                        filter=order_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))

        # Annotate with the number of 'suppliers'
        queryset = queryset.annotate(suppliers=Coalesce(
            SubqueryCount('supplier_parts'),
            Decimal(0),
            output_field=models.DecimalField(),
        ), )

        return queryset
Example #3
0
def profile(request, username, template='profile.html', extra_context=None):
    coder = get_object_or_404(Coder, user__username=username)
    statistics = Statistics.objects.filter(account__coders=coder)

    resources = Resource.objects \
        .prefetch_related(Prefetch(
            'account_set',
            queryset=(
                Account.objects
                .filter(coders=coder)
                .order_by('-n_contests')
            ),
            to_attr='coder_accounts',
        )) \
        .annotate(num_contests=SubquerySum('account__n_contests', filter=Q(coders=coder))) \
        .filter(num_contests__gt=0).order_by('-num_contests')

    writers = Contest.objects.filter(writers__coders=coder)
    context = get_profile_context(request, statistics, writers)

    if context['search_resource']:
        resources = resources.filter(host=context['search_resource'])

    context['coder'] = coder
    context['resources'] = resources

    if extra_context is not None:
        context.update(extra_context)
    return render(request, template, context)
Example #4
0
def annotate_sales_order_allocations(reference: str = ''):
    """Annotate the total quantity of each part allocated to sales orders:

    - This function calculates the total part quantity allocated to open sales orders"
    - Finds all sales order allocations for each part (using the provided filter)
    - Aggregates the 'allocated quantity' for each relevent sales order allocation item

    Args:
        reference: The relationship reference of the part from the current model
        order_filter: Q object which defines how to filter the allocation items
    """

    # Order filter only returns incomplete shipments for open orders
    order_filter = Q(
        line__order__status__in=SalesOrderStatus.OPEN,
        shipment__shipment_date=None,
    )

    return Coalesce(
        SubquerySum(
            f'{reference}stock_items__sales_order_allocations__quantity',
            filter=order_filter,
        ),
        Decimal(0),
        output_field=models.DecimalField(),
    )
Example #5
0
    def annotate_queryset(queryset):
        """
        Add some extra annotations to the queryset,
        performing database queries as efficiently as possible.
        """

        # Annotate the queryset with the total allocated to sales orders
        queryset = queryset.annotate(
            allocated=Coalesce(
                SubquerySum('sales_order_allocations__quantity'), Decimal(0)) +
            Coalesce(SubquerySum('allocations__quantity'), Decimal(0)))

        # Annotate the queryset with the number of tracking items
        queryset = queryset.annotate(
            tracking_items=SubqueryCount('tracking_info'))

        return queryset
Example #6
0
    def annotate_queryset(queryset):
        """
        Add some extra annotations to the queryset,
        performing database queries as efficiently as possible.
        """

        # Annotate the queryset with the total allocated to sales orders
        queryset = queryset.annotate(
            allocated=Coalesce(
                SubquerySum('sales_order_allocations__quantity'), Decimal(0)
            ) + Coalesce(
                SubquerySum('allocations__quantity'), Decimal(0)
            )
        )

        # Annotate the queryset with the number of tracking items
        queryset = queryset.annotate(
            tracking_items=SubqueryCount('tracking_info')
        )

        # Add flag to indicate if the StockItem has expired
        queryset = queryset.annotate(
            expired=Case(
                When(
                    StockItem.EXPIRED_FILTER, then=Value(True, output_field=BooleanField()),
                ),
                default=Value(False, output_field=BooleanField())
            )
        )

        # Add flag to indicate if the StockItem is stale
        stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS')
        stale_date = datetime.now().date() + timedelta(days=stale_days)
        stale_filter = StockItem.IN_STOCK_FILTER & ~Q(expiry_date=None) & Q(expiry_date__lt=stale_date)

        queryset = queryset.annotate(
            stale=Case(
                When(
                    stale_filter, then=Value(True, output_field=BooleanField()),
                ),
                default=Value(False, output_field=BooleanField()),
            )
        )

        return queryset
Example #7
0
def annotate_on_order_quantity(reference: str = ''):
    """Annotate the 'on order' quantity for each part in a queryset"""

    # Filter only 'active' purhase orders
    order_filter = Q(order__status__in=PurchaseOrderStatus.OPEN)

    return Coalesce(
        SubquerySum(
            f'{reference}supplier_parts__purchase_order_line_items__quantity',
            filter=order_filter),
        Decimal(0),
        output_field=models.DecimalField()
    ) - Coalesce(
        SubquerySum(
            f'{reference}supplier_parts__purchase_order_line_items__received',
            filter=order_filter),
        Decimal(0),
        output_field=models.DecimalField(),
    )
Example #8
0
    def annotate_queryset(queryset):
        """Add some extra annotations to the queryset.

        Performing database queries as efficiently as possible, to reduce database trips.
        """

        # Annotate with the total number of stock items
        queryset = queryset.annotate(
            stock_item_count=SubqueryCount('stock_items'))

        # Annotate with the total variant stock quantity
        variant_query = part.filters.variant_stock_query()

        queryset = queryset.annotate(
            variant_stock=part.filters.annotate_variant_quantity(
                variant_query, reference='quantity'), )

        # Filter to limit builds to "active"
        build_filter = Q(status__in=BuildStatus.ACTIVE_CODES)

        # Annotate with the total 'building' quantity
        queryset = queryset.annotate(building=Coalesce(
            SubquerySum('builds__quantity', filter=build_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))

        # Annotate with the number of 'suppliers'
        queryset = queryset.annotate(suppliers=Coalesce(
            SubqueryCount('supplier_parts'),
            Decimal(0),
            output_field=models.DecimalField(),
        ), )

        queryset = queryset.annotate(
            ordering=part.filters.annotate_on_order_quantity(),
            in_stock=part.filters.annotate_total_stock(),
            allocated_to_sales_orders=part.filters.
            annotate_sales_order_allocations(),
            allocated_to_build_orders=part.filters.
            annotate_build_order_allocations(),
        )

        # Annotate with the total 'available stock' quantity
        # This is the current stock, minus any allocations
        queryset = queryset.annotate(unallocated_stock=ExpressionWrapper(
            F('in_stock') - F('allocated_to_sales_orders') -
            F('allocated_to_build_orders'),
            output_field=models.DecimalField(),
        ))

        return queryset
Example #9
0
def coders(request, template='coders.html'):
    coders = Coder.objects.select_related('user')
    params = {}

    search = request.GET.get('search')
    if search:
        filt = get_iregex_filter(search, 'username', logger=request.logger)
        coders = coders.filter(filt)

    countries = request.GET.getlist('country')
    countries = set([c for c in countries if c])
    if countries:
        coders = coders.annotate(filter_country=Exists('account', filter=Q(account__country__in=countries)))
        coders = coders.filter(Q(country__in=countries) | Q(filter_country=True))
        params['countries'] = countries

    resources = request.GET.getlist('resource')
    if resources:
        resources = [r for r in resources if r]
        resources = list(Resource.objects.filter(pk__in=resources))
        for r in resources:
            coders = coders.annotate(**{f'{r.pk}_rating': SubqueryMax('account__rating', filter=Q(resource=r))})
            coders = coders.annotate(**{f'{r.pk}_n_contests': SubquerySum('account__n_contests', filter=Q(resource=r))})
        params['resources'] = resources

    # ordering
    orderby = request.GET.get('sort_column')
    if orderby in ['username', 'created', 'n_accounts']:
        pass
    elif orderby and orderby.startswith('resource_'):
        _, pk = orderby.split('_')
        orderby = [f'{pk}_rating', f'{pk}_n_contests']
    elif orderby:
        request.logger.error(f'Not found `{orderby}` column for sorting')
        orderby = []
    orderby = orderby if not orderby or isinstance(orderby, list) else [orderby]
    order = request.GET.get('sort_order')
    if order in ['asc', 'desc']:
        orderby = [getattr(F(o), order)(nulls_last=True) for o in orderby]
    elif order:
        request.logger.error(f'Not found `{order}` order for sorting')
    orderby = orderby or ['-created']
    coders = coders.order_by(*orderby)

    context = {
        'coders': coders,
        'params': params,
    }
    return template, context
Example #10
0
def update_coder_n_accounts_and_n_contests(signal, instance, action, reverse,
                                           pk_set, **kwargs):
    when, action = action.split('_', 1)
    if when != 'post' or action not in ['add', 'remove']:
        return

    if reverse:
        instance.n_accounts = instance.account_set.count()
        instance.n_contests = instance.account_set.aggregate(
            total=Sum('n_contests'))['total'] or 0
        instance.save()
    else:
        Coder.objects.filter(pk__in=pk_set) \
            .annotate(n_a=SubqueryCount('account')) \
            .annotate(n_c=SubquerySum('account__n_contests')) \
            .update(n_accounts=F('n_a'), n_contests=Coalesce('n_c', 0))
Example #11
0
def annotate_total_stock(reference: str = ''):
    """Annotate 'total stock' quantity against a queryset:

    - This function calculates the 'total stock' for a given part
    - Finds all stock items associated with each part (using the provided filter)
    - Aggregates the 'quantity' of each relevent stock item

    Args:
        reference: The relationship reference of the part from the current model e.g. 'part'
        stock_filter: Q object which defines how to filter the stock items
    """

    # Stock filter only returns 'in stock' items
    stock_filter = stock.models.StockItem.IN_STOCK_FILTER

    return Coalesce(
        SubquerySum(
            f'{reference}stock_items__quantity',
            filter=stock_filter,
        ),
        Decimal(0),
        output_field=models.DecimalField(),
    )
Example #12
0
def annotate_build_order_allocations(reference: str = ''):
    """Annotate the total quantity of each part allocated to build orders:

    - This function calculates the total part quantity allocated to open build orders
    - Finds all build order allocations for each part (using the provided filter)
    - Aggregates the 'allocated quantity' for each relevent build order allocation item

    Args:
        reference: The relationship reference of the part from the current model
        build_filter: Q object which defines how to filter the allocation items
    """

    # Build filter only returns 'active' build orders
    build_filter = Q(build__status__in=BuildStatus.ACTIVE_CODES)

    return Coalesce(
        SubquerySum(
            f'{reference}stock_items__allocations__quantity',
            filter=build_filter,
        ),
        Decimal(0),
        output_field=models.DecimalField(),
    )
Example #13
0
 def get_ordered_resources(self):
     return Resource.objects \
         .annotate(n=SubquerySum('account__n_contests', filter=Q(coders=self))) \
         .order_by(F('n').desc(nulls_last=True), '-has_rating_history', '-n_contests')
Example #14
0
 def test_foreign_key_to_field(self):
     brands = Brand.objects.annotate(
         purchase_sum=SubquerySum('products__num_purchases')
     )
     self.assertEqual(brands.first().purchase_sum, 4)
Example #15
0
    def annotate_queryset(queryset):
        """
        Add some extra annotations to the queryset,
        performing database queries as efficiently as possible,
        to reduce database trips.
        """

        # Annotate with the total 'in stock' quantity
        queryset = queryset.annotate(in_stock=Coalesce(
            SubquerySum('stock_items__quantity',
                        filter=StockItem.IN_STOCK_FILTER),
            Decimal(0),
            output_field=models.DecimalField(),
        ), )

        # Annotate with the total number of stock items
        queryset = queryset.annotate(
            stock_item_count=SubqueryCount('stock_items'))

        # Annotate with the total variant stock quantity
        variant_query = StockItem.objects.filter(
            part__tree_id=OuterRef('tree_id'),
            part__lft__gt=OuterRef('lft'),
            part__rght__lt=OuterRef('rght'),
        ).filter(StockItem.IN_STOCK_FILTER)

        queryset = queryset.annotate(variant_stock=Coalesce(
            Subquery(
                variant_query.annotate(total=Func(
                    F('quantity'), function='SUM',
                    output_field=FloatField())).values('total')),
            0,
            output_field=FloatField(),
        ))

        # Filter to limit builds to "active"
        build_filter = Q(status__in=BuildStatus.ACTIVE_CODES)

        # Annotate with the total 'building' quantity
        queryset = queryset.annotate(building=Coalesce(
            SubquerySum('builds__quantity', filter=build_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))

        # Filter to limit orders to "open"
        order_filter = Q(order__status__in=PurchaseOrderStatus.OPEN)

        # Annotate with the total 'on order' quantity
        queryset = queryset.annotate(ordering=Coalesce(
            SubquerySum('supplier_parts__purchase_order_line_items__quantity',
                        filter=order_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ) - Coalesce(
            SubquerySum('supplier_parts__purchase_order_line_items__received',
                        filter=order_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))

        # Annotate with the number of 'suppliers'
        queryset = queryset.annotate(suppliers=Coalesce(
            SubqueryCount('supplier_parts'),
            Decimal(0),
            output_field=models.DecimalField(),
        ), )
        """
        Annotate with the number of stock items allocated to sales orders.
        This annotation is modeled on Part.sales_order_allocations() method:

        - Only look for "open" orders
        - Stock items have not been "shipped"
        """
        so_allocation_filter = Q(
            line__order__status__in=SalesOrderStatus.
            OPEN,  # LineItem points to an OPEN order
            shipment__shipment_date=
            None,  # Allocated item has *not* been shipped out
        )

        queryset = queryset.annotate(allocated_to_sales_orders=Coalesce(
            SubquerySum('stock_items__sales_order_allocations__quantity',
                        filter=so_allocation_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))
        """
        Annotate with the number of stock items allocated to build orders.
        This annotation is modeled on Part.build_order_allocations() method
        """
        bo_allocation_filter = Q(build__status__in=BuildStatus.ACTIVE_CODES, )

        queryset = queryset.annotate(allocated_to_build_orders=Coalesce(
            SubquerySum('stock_items__allocations__quantity',
                        filter=bo_allocation_filter),
            Decimal(0),
            output_field=models.DecimalField(),
        ))

        # Annotate with the total 'available stock' quantity
        # This is the current stock, minus any allocations
        queryset = queryset.annotate(unallocated_stock=ExpressionWrapper(
            F('in_stock') - F('allocated_to_sales_orders') -
            F('allocated_to_build_orders'),
            output_field=models.DecimalField(),
        ))

        return queryset
Example #16
0
 def get_queryset(self):
     return core.models.Space.objects.owned(self.request.user).annotate(
         files_count=SubqueryCount('files'),
         files_total_size=SubquerySum('files__content_length'),
     )
Example #17
0
    def annotate_queryset(queryset):
        """
        Annotate the BomItem queryset with extra information:

        Annotations:
            available_stock: The amount of stock available for the sub_part Part object
        """
        """
        Construct an "available stock" quantity:
        available_stock = total_stock - build_order_allocations - sales_order_allocations
        """

        build_order_filter = Q(build__status__in=BuildStatus.ACTIVE_CODES)
        sales_order_filter = Q(
            line__order__status__in=SalesOrderStatus.OPEN,
            shipment__shipment_date=None,
        )

        # Calculate "total stock" for the referenced sub_part
        # Calculate the "build_order_allocations" for the sub_part
        # Note that these fields are only aliased, not annotated
        queryset = queryset.alias(
            total_stock=Coalesce(
                SubquerySum('sub_part__stock_items__quantity',
                            filter=StockItem.IN_STOCK_FILTER),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            allocated_to_sales_orders=Coalesce(
                SubquerySum(
                    'sub_part__stock_items__sales_order_allocations__quantity',
                    filter=sales_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            allocated_to_build_orders=Coalesce(
                SubquerySum(
                    'sub_part__stock_items__allocations__quantity',
                    filter=build_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
        )

        # Calculate 'available_stock' based on previously annotated fields
        queryset = queryset.annotate(available_stock=ExpressionWrapper(
            F('total_stock') - F('allocated_to_sales_orders') -
            F('allocated_to_build_orders'),
            output_field=models.DecimalField(),
        ))

        # Extract similar information for any 'substitute' parts
        queryset = queryset.alias(
            substitute_stock=Coalesce(
                SubquerySum(
                    'substitutes__part__stock_items__quantity',
                    filter=StockItem.IN_STOCK_FILTER,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            substitute_build_allocations=Coalesce(
                SubquerySum(
                    'substitutes__part__stock_items__allocations__quantity',
                    filter=build_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            substitute_sales_allocations=Coalesce(
                SubquerySum(
                    'substitutes__part__stock_items__sales_order_allocations__quantity',
                    filter=sales_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
        )

        # Calculate 'available_substitute_stock' field
        queryset = queryset.annotate(
            available_substitute_stock=ExpressionWrapper(
                F('substitute_stock') - F('substitute_build_allocations') -
                F('substitute_sales_allocations'),
                output_field=models.DecimalField(),
            ))

        # Annotate the queryset with 'available variant stock' information
        variant_stock_query = StockItem.objects.filter(
            part__tree_id=OuterRef('sub_part__tree_id'),
            part__lft__gt=OuterRef('sub_part__lft'),
            part__rght__lt=OuterRef('sub_part__rght'),
        ).filter(StockItem.IN_STOCK_FILTER)

        queryset = queryset.alias(
            variant_stock_total=Coalesce(Subquery(
                variant_stock_query.annotate(total=Func(
                    F('quantity'), function='SUM',
                    output_field=FloatField())).values('total')),
                                         0,
                                         output_field=FloatField()),
            variant_stock_build_order_allocations=Coalesce(
                Subquery(
                    variant_stock_query.annotate(total=Func(
                        F('sales_order_allocations__quantity'),
                        function='SUM',
                        output_field=FloatField()), ).values('total')),
                0,
                output_field=FloatField(),
            ),
            variant_stock_sales_order_allocations=Coalesce(
                Subquery(
                    variant_stock_query.annotate(total=Func(
                        F('allocations__quantity'),
                        function='SUM',
                        output_field=FloatField()), ).values('total')),
                0,
                output_field=FloatField(),
            ))

        queryset = queryset.annotate(available_variant_stock=ExpressionWrapper(
            F('variant_stock_total') -
            F('variant_stock_build_order_allocations') -
            F('variant_stock_sales_order_allocations'),
            output_field=FloatField(),
        ))

        return queryset