Exemple #1
0
    def report2(self, ending=None, step=1, length=12, billed=True):
        total = []
        ending = parse_date(ending)

        for date in iter_dates(ending, step, length):
            total = [(date[0], self.period_report(date, fields, billed)),] + total

        return total
Exemple #2
0
    def bc(self, date=None, step=1, length=12, invalidate=None):
        """
        Given a starting date, walk back 'length' in time, and return
        BC-based results for a given model aggregating month values at
        each 'step'.

        eg. model.months.bc('2011.07', 1, 12)
            Returns BC values for the 12 months before July 2011.

            model.months.bc('2011.08', 3, 12)
            Returns BC values for 4 quarters ending in August 2011.

        """

        # setup date
        date = parse_date(date, self.latest)
        year, month = date.year, date.month

        # setup cache key and try to fetch result
        datestamp = date.strftime('%Y.%m')
        cache_key = 'months.bc.%s.len%s.step%s' % (datestamp, length, step)

        if invalidate is None:
            invalidate = self.invalidate_flag

        if invalidate:
            final = self.instance.del_cache(cache_key)
        else:
            final = self.instance.get_cache(cache_key)

        # build result
        if not final:
            final = {self.instance.pk: {
                        'json': {},
                        'name': self.instance.name,
                        'step': step,
                        'length': length,
                     }}

        # set the 'length' to no less than 'step' to prevent empty results
        length = step if step > length else length

        for date_to, date_from in iter_dates(date, step, length):
            result = self.bc_single(date_to, date_from, invalidate)
            datestamp = date_to.strftime("%Y.%m")
            final[self.instance.pk]['json'].update({datestamp: result})

        return final
Exemple #3
0
    def invalidate(self, date=None):
        if not date:
            date = Entry.objects.latest().date

        for x, y in iter_dates(date=date, length=15, as_instance=True):
            datestamp = '%s.%02d' % (x.year, x.month)
            cc = (
                'months.bc.single.',
                'months.bc.',
            )

            for c in cc:
                self.instance.del_cache(c + datestamp)

        # set this flag to pass to each function with params once.
        self.invalidate_flag = True
Exemple #4
0
    def _value(self, date=None, step=1, length=12, agg=False,
               qs_filter=None):
        result = {}
        dates = []
        date = parse_date(date, Entry.objects.latest().date)

        for before, after in iter_dates(date, step, length):
            try:
                qs = self.qs
                if qs_filter is not None:
                    qs = self.qs.filter(qs_filter)
                qs = qs.filter(date__lte=before, date__gte=after)
            except ObjectDoesNotExist:
                qs = Entry.objects.none()

            key = '%s.%02d' % (before.year, before.month)
            dates += [key]

            # yield a value for each month
            if not agg:
                value = self.queryset(before.year, before.month)
            else:
                value = qs.aggregate(Sum('value'))['value__sum']

            result.update({key: value})
            if not value:
                result.update({key: {}})

        nozero = lambda x: Decimal(0) if not x else x
        if not agg:
            base = SortedDict({})
            for d, lines in result.items():
                for line, value in lines.items():
                    current = base.get(line, SortedDict({}))
                    current.update({d: nozero(value)})
                    base.update({line: current})
            # base = {k: {kk:vv for kk, nozero(vv) in v} for k, v in result.items()}
        else:
            base = {k: v for k, v in result.items()}

        for k, v in base.items():
            yield {k: v}
Exemple #5
0
    def report(self, ending=None, step=1, length=12, fields=None, billed=True):
        if step > length:
            length = step

        total = {}
        extracted = {}
        ending = parse_date(ending)

        print (ending, step, length)
        for date in iter_dates(ending, step, length):
            # [{'code': 123, 'value': 10}, {'code': 234, 'value': 2}]
            period_report = self.period_report(date, fields=fields, billed=billed)
            # {'123': {'value': 10}, '234': {'value': 2}}
            period_formatted = {}
            for d in period_report: # d means dict
                for key in ('billed_corp', 'billed_cons', 'total'):
                    # let's convert these to floats, adding 0s where needed
                    if not d.get(key, None):
                        d[key] = 0.0
                    else:
                        d[key] = float(d[key])

                d['billed'] = d.get('billed_corp', 0.0) + d.get('billed_cons', 0.0)
                if d['billed_cons'] == 0.0 or d['billed_corp'] == 0.0:
                    cons_index = d['cons_index']
                else:
                    try:
                        cons_index = d['billed_cons'] / d['billed']
                    except ZeroDivisionError:
                        cons_index = 0.5

                corp_index = 1 - cons_index
                d['corp'] = d['total'] * corp_index
                d['cons'] = d['total'] * cons_index

                d['cons_index'] = cons_index
                d['corp_index'] = corp_index

                # period_formatted[acc]['json'].update()

                # fields is a dict that contains a mapping of the extra
                # fields to be include and a choice of field name
                # represented by an F object or a permanent value
                #
                # these are all valid:
                #     {'parent_id': F('group__parent_id')}
                #     {'parent_id': 9}
                #     {'level': 'foo'}
                #

                code = d.pop('code')
                total[code] = total.get(code, {'json': {}})
                if fields is not None:
                    for key, value in fields.items():
                        if isinstance(value, F):
                            total[code][key] = d.get(value.name)
                        else:
                            total[code][key] = value

                nice_date = date[1].strftime("%Y-%m")

                # period_formatted[code][nice_date] = d

                total[code]['json'][nice_date] = d


        return total
Exemple #6
0
    def bc(self, date=None, step=1, length=12, invalidate=None):
        "Get BC-indexed results for every child."

        # setup date
        date = parse_date(date, self.latest)
        year, month = date.year, date.month

        # setup cache key and try to fetch result
        datestamp = date.strftime('%Y.%m')
        # cache_key = 'months.bc.' + datestamp
        cache_key = 'months.bc.%s.len%s.step%s' % (datestamp, length, step)

        if invalidate is None:
            invalidate = self.invalidate_flag

        if invalidate:
            final = self.instance.del_cache(cache_key)
        else:
            final = self.instance.get_cache(cache_key)


        if not final:
            final = {'step': step,
                     'length': length,
                     'json': {}}

        # set the 'length' to no less than 'step' to prevent empty results
        length = step if step > length else length

        # fill in placeholders for the result
        keys = ['cons_sum', 'cons_index', 'cons_result',
                'corp_sum', 'corp_index', 'corp_result', 'total']

        result = {}
        empty_dict = {}
        for yielded, nothing in iter_dates(date, step, length):
            datestr = yielded.strftime('%Y.%m')
            empty_dict[datestr] = {}
            for k in keys:
                empty_dict[datestr].update({k: 0.0})

        # descendants = self.instance.get_descendants(include_self=True)\
                          # .values('pk', 'name')
        # descendants_pk = [pk for pk, name in [obj.values() for obj in descendants]]
        # desc = [(pk, name) for pk, name in [obj.values() for obj in descendants]]

        descendants = self.instance.get_descendants(include_self=True)
        descendants_pk = []
        from copy import deepcopy
        for group in descendants:
            descendants_pk += [group.pk]
            result.update({group.pk: {
                'json': deepcopy(empty_dict),
                'name':group.name,
                'lft':group.lft,
                'group_id': group.pk,
                'parent_id': group.parent_id,
                'level': group.level
                }})

        # fill in results from accounts
        for acc in self.accounts:
            acc_result = acc.months.bc(date, step, length, invalidate)
            # FIXME: is this the best way to pop 'length' and 'date'
            # out of the account dict.
            acc_result[acc.pk].pop('step')
            acc_result[acc.pk].pop('length')
            acc_result[acc.pk].update({
                'level': acc.group.level +1,
                'lft': 9000,
                'parent_id': acc.group_id,
                'group_id': acc.group_id,
            })
            result.update(**acc_result)

            # -----------------------------------------------------------
            # for each group to which this account is a descendant of,
            # add the account's value to that group's result

            # some of the account's groups might be above self.instance,
            # so filter on descendants too
            _groups = acc.groups().filter(pk__in=descendants_pk).values_list('pk', flat=True)
            for pk in _groups:
                group_result = result[pk]['json']
                for d, values in group_result.items():
                    for value_name in values:
                        try:
                            v = float(acc_result[acc.pk]['json'][d][value_name])
                        except:
                            v = 0.0
                        vv = result[pk]['json'][d][value_name]
                        result[pk]['json'][d][value_name] = vv + v

        # get account groups only
        # regex = re.compile('\d+')
        # for pk in [k for k in result.keys() if regex.match(k)]:

        # iterator = [obj, obj['json'] for obj in result]
        for pk, json in [(obj[0], obj[1]['json']) for obj in result.items()]:
            for d, values in json.items():
                # cast these string back to float first
                cons_sum = float(values['cons_sum'])
                corp_sum = float(values['corp_sum'])
                cons_result = float(values['cons_result'])
                corp_result = float(values['corp_result'])
                bc_total = cons_sum + corp_sum
                total = float(values['total'])

                try:
                    cons_index = cons_sum / bc_total
                except ZeroDivisionError:
                    cons_index = 0
                try:
                    corp_index = corp_sum / bc_total
                except ZeroDivisionError:
                    corp_index = 0

                if corp_index < 0 or cons_index > 1:
                    corp_index = 0
                    cons_index = 1
                elif cons_index < 0 or corp_index > 1:
                    cons_index = 0
                    corp_index = 1

                result[pk]['json'][d].update({
                    "cons_sum": '%02.3f' % cons_sum,
                    "cons_index": '%02.2f' % cons_index,
                    "cons_result": '%02.3f' % cons_result,
                    "corp_sum": '%02.3f' % corp_sum,
                    "corp_index": '%02.2f' % corp_index,
                    "corp_result": '%02.3f' % corp_result,
                    "total": '%02.3f' % total,
                    })

        self.result = result
        return self.result