Пример #1
0
def aggregate_monthly_churn(organization,
                            account,
                            interval,
                            from_date=None,
                            orig='orig',
                            dest='dest'):
    """
    Returns a table of records over a period of 12 months *from_date*.
    """
    #pylint: disable=too-many-locals,too-many-arguments
    customers = []
    receivables = []
    new_customers = []
    new_receivables = []
    churn_customers = []
    churn_receivables = []
    # We want to be able to compare *last* to *from_date* and not get django
    # warnings because timezones are not specified.
    dates = month_periods(13, from_date)
    trail_period_start = dates[0]
    period_start = dates[1]
    for period_end in dates[2:]:
        if interval == Plan.YEARLY:
            prev_period_start = datetime(day=period_start.day,
                                         month=period_start.month,
                                         year=period_start.year - 1,
                                         tzinfo=period_start.tzinfo)
            prev_period_end = datetime(day=period_end.day,
                                       month=period_end.month,
                                       year=period_end.year - 1,
                                       tzinfo=period_end.tzinfo)
        else:
            # default to monthly
            prev_period_start = trail_period_start
            prev_period_end = period_start
        churn_query = RawQuery(
            """SELECT COUNT(DISTINCT(prev.%(dest)s_organization_id)),
          SUM(prev.%(dest)s_amount)
       FROM saas_transaction prev
       LEFT OUTER JOIN (
         SELECT distinct(%(dest)s_organization_id)
           FROM saas_transaction
           WHERE created_at >= '%(period_start)s'
         AND created_at < '%(period_end)s'
         AND %(orig)s_organization_id = '%(organization_id)s'
         AND %(orig)s_account = '%(account)s') curr
         ON prev.%(dest)s_organization_id = curr.%(dest)s_organization_id
       WHERE prev.created_at >= '%(prev_period_start)s'
         AND prev.created_at < '%(prev_period_end)s'
         AND prev.%(orig)s_organization_id = '%(organization_id)s'
         AND prev.%(orig)s_account = '%(account)s'
         AND curr.%(dest)s_organization_id IS NULL""" % {
                "orig": orig,
                "dest": dest,
                "prev_period_start": prev_period_start,
                "prev_period_end": prev_period_end,
                "period_start": period_start,
                "period_end": period_end,
                "organization_id": organization.id,
                "account": account
            }, router.db_for_read(Transaction))
        churn_customer, churn_receivable = next(iter(churn_query))
        # A bit ugly but it does the job ...
        if orig == 'orig':
            kwargs = {
                'orig_organization': organization,
                'orig_account': account
            }
        else:
            kwargs = {
                'dest_organization': organization,
                'dest_account': account
            }
        query_result = Transaction.objects.filter(
            created_at__gte=period_start, created_at__lt=period_end,
            **kwargs).aggregate(Count('%s_organization' % dest, distinct=True),
                                Sum('%s_amount' % dest))
        customer = query_result['%s_organization__count' % dest]
        receivable = query_result['%s_amount__sum' % dest]
        new_query = RawQuery(
            """SELECT count(distinct(curr.%(dest)s_organization_id)),
          SUM(curr.%(dest)s_amount)
   FROM saas_transaction curr
       LEFT OUTER JOIN (
         SELECT distinct(%(dest)s_organization_id)
           FROM saas_transaction
           WHERE created_at >= '%(prev_period_start)s'
         AND created_at < '%(prev_period_end)s'
         AND %(orig)s_organization_id = '%(organization_id)s'
         AND %(orig)s_account = '%(account)s') prev
         ON curr.%(dest)s_organization_id = prev.%(dest)s_organization_id
       WHERE curr.created_at >= '%(period_start)s'
         AND curr.created_at < '%(period_end)s'
         AND curr.%(orig)s_organization_id = '%(organization_id)s'
         AND curr.%(orig)s_account = '%(account)s'
         AND prev.%(dest)s_organization_id IS NULL""" % {
                "orig": orig,
                "dest": dest,
                "prev_period_start": prev_period_start,
                "prev_period_end": prev_period_end,
                "period_start": period_start,
                "period_end": period_end,
                "organization_id": organization.id,
                "account": account
            }, router.db_for_read(Transaction))
        new_customer, new_receivable = next(iter(new_query))
        period = period_end
        churn_customers += [(period, churn_customer)]
        churn_receivables += [(period, int(churn_receivable or 0))]
        customers += [(period, customer)]
        receivables += [(period, int(receivable or 0))]
        new_customers += [(period, new_customer)]
        new_receivables += [(period, int(new_receivable or 0))]
        trail_period_start = period_start
        period_start = period_end
    return ((churn_customers, customers, new_customers),
            (churn_receivables, receivables, new_receivables))
Пример #2
0
def _aggregate_transactions_change_by_period(organization,
                                             account,
                                             date_periods,
                                             orig='orig',
                                             dest='dest'):
    """
    Returns a table of records over a period of 12 months *from_date*.
    """
    #pylint:disable=too-many-locals,too-many-arguments,too-many-statements
    #pylint:disable=invalid-name
    customers = []
    receivables = []
    new_customers = []
    new_receivables = []
    churn_customers = []
    churn_receivables = []
    unit = None
    period_start = date_periods[0]
    for period_end in date_periods[1:]:
        delta = Plan.get_natural_period(1, organization.natural_interval)
        prev_period_end = period_end - delta
        prev_period_start = prev_period_end - relativedelta(
            period_end, period_start)
        LOGGER.debug(
            "computes churn between periods ['%s', '%s'] and ['%s', '%s']",
            prev_period_start.isoformat(), prev_period_end.isoformat(),
            period_start.isoformat(), period_end.isoformat())
        try:
            churn_query = RawQuery(
                """SELECT COUNT(DISTINCT(prev.%(dest)s_organization_id)),
              SUM(prev.%(dest)s_amount),
              prev.%(dest)s_unit
           FROM saas_transaction prev
           LEFT OUTER JOIN (
             SELECT distinct(%(dest)s_organization_id), %(orig)s_unit
               FROM saas_transaction
               WHERE created_at >= '%(period_start)s'
             AND created_at < '%(period_end)s'
             AND %(orig)s_organization_id = '%(organization_id)s'
             AND %(orig)s_account = '%(account)s'
             ) curr
             ON prev.%(dest)s_organization_id = curr.%(dest)s_organization_id
           WHERE prev.created_at >= '%(prev_period_start)s'
             AND prev.created_at < '%(prev_period_end)s'
             AND prev.%(orig)s_organization_id = '%(organization_id)s'
             AND prev.%(orig)s_account = '%(account)s'
             AND curr.%(dest)s_organization_id IS NULL
             GROUP BY prev.%(dest)s_unit
             """ % {
                    "orig": orig,
                    "dest": dest,
                    "prev_period_start": prev_period_start,
                    "prev_period_end": prev_period_end,
                    "period_start": period_start,
                    "period_end": period_end,
                    "organization_id": organization.id,
                    "account": account
                }, router.db_for_read(Transaction))
            churn_customer, churn_receivable, churn_receivable_unit = next(
                iter(churn_query))
            if churn_receivable_unit:
                unit = churn_receivable_unit
        except StopIteration:
            churn_customer, churn_receivable, churn_receivable_unit = 0, 0, None

        # A bit ugly but it does the job ...
        if orig == 'orig':
            kwargs = {
                'orig_organization': organization,
                'orig_account': account
            }
        else:
            kwargs = {
                'dest_organization': organization,
                'dest_account': account
            }

        customer = 0
        receivable = 0
        receivable_unit = None
        query_result = Transaction.objects.filter(
            created_at__gte=period_start, created_at__lt=period_end,
            **kwargs).values('%s_unit' % dest).annotate(
                count=Count('%s_organization' % dest, distinct=True),
                sum=Sum('%s_amount' % dest))
        if query_result:
            customer = query_result[0]['count']
            receivable = query_result[0]['sum']
            receivable_unit = query_result[0]['%s_unit' % dest]
            if receivable_unit:
                unit = receivable_unit

        try:
            new_query = RawQuery(
                """SELECT count(distinct(curr.%(dest)s_organization_id)),
              SUM(curr.%(dest)s_amount),
              curr.%(dest)s_unit
       FROM saas_transaction curr
           LEFT OUTER JOIN (
             SELECT distinct(%(dest)s_organization_id)
               FROM saas_transaction
               WHERE created_at >= '%(prev_period_start)s'
             AND created_at < '%(prev_period_end)s'
             AND %(orig)s_organization_id = '%(organization_id)s'
             AND %(orig)s_account = '%(account)s') prev
             ON curr.%(dest)s_organization_id = prev.%(dest)s_organization_id
           WHERE curr.created_at >= '%(period_start)s'
             AND curr.created_at < '%(period_end)s'
             AND curr.%(orig)s_organization_id = '%(organization_id)s'
             AND curr.%(orig)s_account = '%(account)s'
             AND prev.%(dest)s_organization_id IS NULL
             GROUP BY curr.%(dest)s_unit""" % {
                    "orig": orig,
                    "dest": dest,
                    "prev_period_start": prev_period_start,
                    "prev_period_end": prev_period_end,
                    "period_start": period_start,
                    "period_end": period_end,
                    "organization_id": organization.id,
                    "account": account
                }, router.db_for_read(Transaction))
            new_customer, new_receivable, new_receivable_unit = next(
                iter(new_query))
            if new_receivable_unit:
                unit = new_receivable_unit
        except StopIteration:
            new_customer, new_receivable, new_receivable_unit = 0, 0, None

        units = get_different_units(churn_receivable_unit, receivable_unit,
                                    new_receivable_unit)
        if len(units) > 1:
            LOGGER.error("different units: %s", units)

        period = period_end
        churn_customers += [(period, churn_customer)]
        churn_receivables += [(period, int(churn_receivable or 0))]
        customers += [(period, customer)]
        receivables += [(period, int(receivable or 0))]
        new_customers += [(period, new_customer)]
        new_receivables += [(period, int(new_receivable or 0))]
        period_start = period_end

    return ((churn_customers, customers, new_customers),
            (churn_receivables, receivables, new_receivables), unit)
Пример #3
0
def _aggregate_transactions_change_by_period(organization,
                                             account,
                                             date_periods,
                                             orig='orig',
                                             dest='dest'):
    """
    Returns a table of records over a period of 12 months *from_date*.
    """
    #pylint: disable=too-many-locals,too-many-arguments,invalid-name
    customers = []
    receivables = []
    new_customers = []
    new_receivables = []
    churn_customers = []
    churn_receivables = []
    period_start = date_periods[0]
    for period_end in date_periods[1:]:
        delta = Plan.get_natural_period(1, organization.natural_interval)
        prev_period_end = period_end - delta
        prev_period_start = prev_period_end - relativedelta(
            period_end, period_start)
        LOGGER.debug(
            "computes churn between periods ['%s', '%s'] and ['%s', '%s']",
            prev_period_start.isoformat(), prev_period_end.isoformat(),
            period_start.isoformat(), period_end.isoformat())
        churn_query = RawQuery(
            """SELECT COUNT(DISTINCT(prev.%(dest)s_organization_id)),
          SUM(prev.%(dest)s_amount)
       FROM saas_transaction prev
       LEFT OUTER JOIN (
         SELECT distinct(%(dest)s_organization_id)
           FROM saas_transaction
           WHERE created_at >= '%(period_start)s'
         AND created_at < '%(period_end)s'
         AND %(orig)s_organization_id = '%(organization_id)s'
         AND %(orig)s_account = '%(account)s') curr
         ON prev.%(dest)s_organization_id = curr.%(dest)s_organization_id
       WHERE prev.created_at >= '%(prev_period_start)s'
         AND prev.created_at < '%(prev_period_end)s'
         AND prev.%(orig)s_organization_id = '%(organization_id)s'
         AND prev.%(orig)s_account = '%(account)s'
         AND curr.%(dest)s_organization_id IS NULL""" % {
                "orig": orig,
                "dest": dest,
                "prev_period_start": prev_period_start,
                "prev_period_end": prev_period_end,
                "period_start": period_start,
                "period_end": period_end,
                "organization_id": organization.id,
                "account": account
            }, router.db_for_read(Transaction))
        churn_customer, churn_receivable = next(iter(churn_query))
        # A bit ugly but it does the job ...
        if orig == 'orig':
            kwargs = {
                'orig_organization': organization,
                'orig_account': account
            }
        else:
            kwargs = {
                'dest_organization': organization,
                'dest_account': account
            }
        query_result = Transaction.objects.filter(
            created_at__gte=period_start, created_at__lt=period_end,
            **kwargs).aggregate(Count('%s_organization' % dest, distinct=True),
                                Sum('%s_amount' % dest))
        customer = query_result['%s_organization__count' % dest]
        receivable = query_result['%s_amount__sum' % dest]
        new_query = RawQuery(
            """SELECT count(distinct(curr.%(dest)s_organization_id)),
          SUM(curr.%(dest)s_amount)
   FROM saas_transaction curr
       LEFT OUTER JOIN (
         SELECT distinct(%(dest)s_organization_id)
           FROM saas_transaction
           WHERE created_at >= '%(prev_period_start)s'
         AND created_at < '%(prev_period_end)s'
         AND %(orig)s_organization_id = '%(organization_id)s'
         AND %(orig)s_account = '%(account)s') prev
         ON curr.%(dest)s_organization_id = prev.%(dest)s_organization_id
       WHERE curr.created_at >= '%(period_start)s'
         AND curr.created_at < '%(period_end)s'
         AND curr.%(orig)s_organization_id = '%(organization_id)s'
         AND curr.%(orig)s_account = '%(account)s'
         AND prev.%(dest)s_organization_id IS NULL""" % {
                "orig": orig,
                "dest": dest,
                "prev_period_start": prev_period_start,
                "prev_period_end": prev_period_end,
                "period_start": period_start,
                "period_end": period_end,
                "organization_id": organization.id,
                "account": account
            }, router.db_for_read(Transaction))
        new_customer, new_receivable = next(iter(new_query))
        period = period_end
        churn_customers += [(period, churn_customer)]
        churn_receivables += [(period, int(churn_receivable or 0))]
        customers += [(period, customer)]
        receivables += [(period, int(receivable or 0))]
        new_customers += [(period, new_customer)]
        new_receivables += [(period, int(new_receivable or 0))]
        period_start = period_end
    return ((churn_customers, customers, new_customers),
            (churn_receivables, receivables, new_receivables))