# Path: anubis/sql_aggregators.py
# class ProcedureOrderingAnnotation(RawSQL):
#     template = (
#         "(select {function}_res.rank "
#         "from {function}({args}) as {function}_res "
#         "where {field} = {function}_res.id)"
#     )
# 
#     def __init__(self, function, *args, lookup="id", **kwargs):
#         self.name = function
#         self.col_name = lookup
#         self.col = self._parse_expressions(lookup)[0]
# 
#         super().__init__("", args)
# 
#     def resolve_expression(self, query=None, allow_joins=True, reuse=None,
#                            summarize=False, for_save=False):
#         clone = self.copy()
#         clone.is_summary = summarize
#         clone.col = clone.col.resolve_expression(query, allow_joins, reuse,
#                                                  summarize, for_save)
# 
#         return clone
# 
#     @property
#     def default_alias(self):
#         return "{}_{}".format(self.col_name, self.name.lower())
# 
#     def as_sql(self, compiler, connection):
#         field_name, _ = compiler.compile(self.col)
# 
#         args = list(self.params)
# 
#         arg_marks = ", ".join(["%s"] * len(args))
# 
#         self.sql = self.template.format(function=self.name, args=arg_marks,
#                                         field=field_name)
# 
#         return super().as_sql(compiler, connection)


from collections import OrderedDict
from operator import itemgetter
from anubis.sql_aggregators import ProcedureOrderingAnnotation
from django.db import connection as base_connection, connections
from django.db import models
from django.db.models.query import QuerySet
from django.db.models.expressions import Ref, F
        aggregate = self.procedure_aggregate(procname, *args, **kwargs)
        return self.order_by_aggregates(aggregate, field=field,
                                        extra_fields=extra_fields)

    def order_by_aggregates(self, *aggregates, field="id", extra_fields=None):
        annotation = OrderedDict([
            ("{}_{}".format(agg.default_alias, i), agg)
            for i, agg in enumerate(aggregates)
        ])

        fields = list(annotation.keys())
        fields = [Ref(f, annotation[f]) for f in fields]

        if extra_fields is not None:
            fields += [F(f) for f in extra_fields]
        else:
            fields.append(F(field))

        return self.annotate(**annotation).order_by(*fields)

    def procedure_aggregate(self, procname, *args, **kwargs):
        """Helper method for generating ordering annotations.

        Returns:
            anubis.sql_aggregators.ProcedureOrderingAnnotation: An ordering
                annotation with the correct procedure name (including table
                name).
        """
        procname = "{}_{}".format(self.model._meta.db_table, procname)


# Path: anubis/sql_aggregators.py
# class ProcedureOrderingAnnotation(RawSQL):
#     template = (
#         "(select {function}_res.rank "
#         "from {function}({args}) as {function}_res "
#         "where {field} = {function}_res.id)"
#     )
# 
#     def __init__(self, function, *args, lookup="id", **kwargs):
#         self.name = function
#         self.col_name = lookup
#         self.col = self._parse_expressions(lookup)[0]
# 
#         super().__init__("", args)
# 
#     def resolve_expression(self, query=None, allow_joins=True, reuse=None,
#                            summarize=False, for_save=False):
#         clone = self.copy()
#         clone.is_summary = summarize
#         clone.col = clone.col.resolve_expression(query, allow_joins, reuse,
#                                                  summarize, for_save)
# 
#         return clone
# 
#     @property
#     def default_alias(self):
#         return "{}_{}".format(self.col_name, self.name.lower())
# 
#     def as_sql(self, compiler, connection):
#         field_name, _ = compiler.compile(self.col)
# 
#         args = list(self.params)
# 
#         arg_marks = ", ".join(["%s"] * len(args))
# 
#         self.sql = self.template.format(function=self.name, args=arg_marks,
#                                         field=field_name)
# 
#         return super().as_sql(compiler, connection)


# Path: anubis/query.py
from collections import OrderedDict
from operator import itemgetter
from anubis.sql_aggregators import ProcedureOrderingAnnotation
from django.db import connection as base_connection, connections
from django.db import models
from django.db.models.query import QuerySet
from django.db.models.expressions import Ref, F

        aggregate = self.procedure_aggregate(procname, *args, **kwargs)
        return self.order_by_aggregates(aggregate, field=field,
                                        extra_fields=extra_fields)

    def order_by_aggregates(self, *aggregates, field="id", extra_fields=None):
        annotation = OrderedDict([
            ("{}_{}".format(agg.default_alias, i), agg)
            for i, agg in enumerate(aggregates)
        ])

        fields = list(annotation.keys())
        fields = [Ref(f, annotation[f]) for f in fields]

        if extra_fields is not None:
            fields += [F(f) for f in extra_fields]
        else:
            fields.append(F(field))

        return self.annotate(**annotation).order_by(*fields)

    def procedure_aggregate(self, procname, *args, **kwargs):
        """Helper method for generating ordering annotations.

        Returns:
            anubis.sql_aggregators.ProcedureOrderingAnnotation: An ordering
                annotation with the correct procedure name (including table
                name).
        """
        procname = "{}_{}".format(self.model._meta.db_table, procname)


        return ProcedureOrderingAnnotation(procname, *args, **kwargs)