Theremin

Материал из Course Orchestra
Перейти к: навигация, поиск

Система расчёта показателей на базе платформы Flute

https://share.curs.ru/svn/apps/calculation

Общие сведения

Система предназначена для вычисления расчётных показателей в аналитическом хранилище многомерных данных. Класс формул для расчётных показателей произволен — от частичных сумм, предназначенных для ускорения OLAP-анализа, до функций, вычисляемых по произвольным алгоритмам.

Исполняемый код системы представляет собой Python-скрипт, который может быть выполнен системой Flute, Showcase или иным способом (рекомендуется Flute). Скрипт состоит из стандартной (не изменяемой) и кастомизируемой части, причём кастомизируемая часть представляет собой объявления классов, сделанные по строго определённым правилам с использованием предоставляемой пользователю библиотеки базовых классов и утилит. Каждый из классов, объявляемый в кастомизируемой части, соответствует одному из значений измерения «показатель», т. е. одному из базовых или расчётных показателей. В объявляемых по определённым правилам атрибутах и методах класса содержится вся информация о показателе и способе его вычисления. Стандартная часть библиотеки содержит методы запуска вычисления расчётных показателей, с возможностью выбора подмножества расчётных показателей для вычисления и подмножества исходных данных.

Система поддерживает те же типы баз данных, что и Celesta:

  • MS SQL Server
  • PostgreSQL
  • Oracle (см. ниже описание метода setDBType).

Ожидаемая структура базы данных и дескриптор структуры базы данных

Система предназначается для установки на уже имеющиеся аналитические хранилища и не требует для своей работы строго определённой структуры таблиц. Вместо этого система предполагает, что подключена к базе данных со структурой определённого вида, таблицы которой выполняют определённые роли (таблицы фактов, справочники значений измерений), но при этом их имена, структура и количество могут варьироваться.

Дескриптор, представляющий собой XML-документ, предназначен для описания структуры имеющейся БД для системы расчёта показателей.

Структура таблиц, описываемая дескриптором, показана на рис. 1.

Calc pic1.png

Рисунок 1: Элементы структуры БД, к которым подключается система расчёта показателей

Таблица фактов является таблицей с аналитическими данными, в ней содержатся исходные данные для расчёта, в неё же записываются расчётные данные. Данная таблица обязана присутствовать в аналитическом хранилище. Система работает только с одной таблицей фактов.

В этой таблице особую роль с точки зрения системы играют следующие поля:

  • поле id — требуется для работы механизма «гибких измерений» (если он используется)
  • поля дат — по сути, представляют собой значения измерений, однако датам, в отличие от других измерений, не требуется справочник значений и Foreign Key на этот справочник. Могут быть двух субтипов:
    • поле даты с типом данных date/datetime, привязывающие факт к конкретному моменту времени. Хотя по измерениям с таким типом можно вычислять частичные суммы, эти измерения не могут участвовать в итерациях вычислений расчётных показателей, т. к. не являются «дискретизируемыми»: с помощью поля с типом datetime невозможно задавать границы и иерархию периодов.
    • поле периода с типом данных varchar(8), служащие для указания определённого периода (дня, месяца, квартала, года) в формате ISO8601. Примеры: 12 января 2012 г.: 20120112, декабрь 2011 г.: 201112, третий квартал 2011 г.: 2011Q3 и т. д. В дескрипторе базы данных определяются уровни иерархии данного измерения, а также начальная и конечная даты (чем определяется полный конечный набор возможных значений таких измерений).
  • поля ссылок на значения «быстрых» измерений — внешние ключи на справочники значений «быстрых» измерений, этих полей может быть произвольное количество.
  • значение измерения «показатель» — особое, системное измерение — по нему идут вычисления
  • поля значений — имеют числовой тип, в таблице фактов может быть сколько угодно значений.

Справочник показателей — справочник с расшифровками кодов показателей. Содержимое этого справочника (если он существует) должно быть синхронизировано с текстом кастомизированной части системы таким образом, чтобы каждая запись в этой таблице соответствовала определённому Python-классу. Данный справочник может и отсутствовать (но обязательно должно присутствовать поле «код показателя» в таблице фактов).

Плоские и иерархические справочники значений «быстрых» измерений — справочники, содержащие расшифровку кодов и произвольную информацию по быстрым измерениям. Т. к. систему вычисления интересуют только коды значений измерений, система не обращается к этим справочникам. Внимание: для иерархических справочников предполагается, что значения их первичного ключа кодируются иерархией Дьюи (кодами вида 11.22.33), и обработка, связанная с иерархией, полагается на особый вид кодов значений измерений. Данная таблица может отсутствовать, если не используется механизм гибких измерений.

Атрибутированные и разреженные измерения – вид быстрых измерений, отбор значений которых происходит не по коду измерения, а по значению какого-то из полей в справочнике значений измерения. Следует понимать, что поддержка атрибутированных измерений возможно только с использованием SQL запросов с конструкциями WHERE...IN, которые могут оказаться не слишком производительными в ряде случаев, и этой функциональностью следует пользоваться с осторожностью.

Справочник «гибких» измерений — помимо фиксированного количества «быстрых», жёстко заданных в структуре БД измерений, аналитическое хранилище может также поддерживать гибко настраиваемые произвольные измерения. Данная таблица хранит перечень таких измерений. Таблица может отсутствовать, если не используется механизм гибких измерений.

Справочник значений «гибких» измерений — содержит в единой таблице значения всех «гибких» измерений. Таблица может отсутствовать, если не используется механизм гибких измерений.

Таблица, связывающая факты со значениями гибких измерений — привязывает к идентификатору факта одну или несколько пар «код гибкого измерения, значение гибкого измерения». Таблица может отсутствовать, если не используется механизм гибких измерений. В базе данных также могут иметься таблицы, связывающие гибкие измерения (в этом случае именуемые «атрибутами») со значениями «быстрых» измерений, однако система предполагает, что при записи факта в таблицу фактов атрибут копируется в таблицу, связывающую атрибуты и факты напрямую.

На практике аналитические хранилища всегда представляют собой некоторый компромисс между использованием «жёстких» и «гибких» измерений, число которых может варьироваться. Тем не менее, какой бы ни была реальная структура аналитического хранилища, логически её можно свести к структуре типа «звезда». Виртуальное, логическое представление аналитического хранилища с точки зрения системы расчётов показателей показано на рис. 2. С точки зрения виртуальной структуры «гибкие» измерения играют ту же роль, что и «быстрые», однако понятно, что для фильтрации данных по их значениям СУБД должна выполнять более сложные запросы и, соответственно, любые вычисления с участием этих измерений являются существенно более ресурсоёмкими.

Типовым сценарием использования «гибких измерений» является привязка значений гибких измерений к значениям быстрых измерений (в этом случае привязанные значения часто именуются «атрибутами»). При этом, при записи факта в таблицу фактов, значения атрибутов должны быть перенесены в таблицу, связывающую факт и значение атрибута напрямую.

Calc pic1.png

Рисунок 2: Виртуальная структура аналитического хранилища после "разворачивания" гибких измерений

Дескриптор структуры базы данных содержит необходимую и достаточную для построения нужных SQL-запросов информацию о привязке актуальных имён таблиц и полей к ролям:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Описание аналитического хранилища -->
<descriptor>
    <!--Описание таблицы фактов. В дескрипторе должен быть один и только один тэг facts-->
    <facts tablename="fact">
        <!-- Поле кода факта. Должно быть одно в таблице фактов -->
        <id>id</id>
        <!-- Поле кода измерения. Должно быть одно в таблице фактов -->
        <figure>figureId</figure>
        
        <!-- Поля дат - допускается произвольное количество, в т. ч. отсутствие -->
        <date>date1</date>
        ...
        <date>dateN</date>

       <!-- Поля периодов - допускается произвольное количество, в т. ч. отсутствие. 
           Атрибутами периода являются: from – дата начала (в формате ISO8601), to — дата окончания, 
           detail — детализация (набор букв YQMD, обозначающих детализацию по году, кварталу, месяцу и дню). -->
        <period from="20110101" to="20111231" detail="YQM">period1</period>
        ...
        <period>periodN</period> 
       
        <!-- Поля измерений - допускается произвольное количество, в т. ч. отсутствие -->
        <dimension>fastDim1</dimension>
        ...
        <dimension>fastDimN</dimension>
        
        <!-- Поля значений - допускается произвольное кол-во, не менее одного -->
        <value>val1</value>
        ...
        <value>valN</value>
    </facts>
    
     <!--Описание справочника значений атрибутированного измерения. Таких тэгов может быть несколько. 
        Атрибут tablename --- это имя таблицы со справочником значений измерения. -->
      <attibuteddim dimid="fdim3" tablename="attdim">
<!--Это поле первичного ключа -- его значения прописываются в поле fdim3 таблицы фактов-->
        <attdimvalue>id</attdimvalue>
         <!-- А это пошли поля атрибутов. Пока что поддерживаются всего два типа значений: 
         числовой и текстовый. Весь "фарш", связанный с датами и периодами, для атрибутов отсутствует. 
         Если нужно будет -- доработаю отдельно.-->
           <attribute>attr1</attribute>
                <attribute fieldtype="numeric">attr2</attribute>
                <attribute>attr3</attribute>
           </attibuteddim> 
    <!-- Описание таблицы со значениями гибких измерений, привязанной к таблице фактов. 
          Допустимо отсутствие или присутствие одной такой таблицы-->
    <factsflexivalues tablename="factdimensions">
        <!-- Поле кода факта -->
        <factid>factid</factid>
        <!-- Поле кода измерения-->
        <dimid>dimid</dimid>
        <!-- Поле кода значения измерения -->
        <dimvalue>dimvalue</dimvalue>
    </factsflexivalues>
</descriptor>

Разреженные измерения

Одним из наиболее востребованных вариантов работы с OLAP-данными является работа с измерениями, снабжёнными атрибутами, и желание фильтровать по атрибутам измерений. Наш расчётный модуль поддерживает такую возможность, однако следует понимать, что бездумное использование этой функции ведёт к слишком долгому времени на расчёт.

В фильтрах, указывая имя измерения, вместо "dimname" можно можно писать так: "dimname:attrname", где идущий после двоеточия attrname — атрибут измерения, т. е. некий столбец справочника значений измерения. Таким образом выходит, что количество измерений, которые можно упомянуть в фильтре, увеличивается на количество атрибутов атрибутированного измерения. А также по-прежнему можно фильтровать по значениям самого измерения.

При этом на таблицу фактов будет формироваться запрос вида

SELECT SUM(...) WHERE dimname IN (SELECT attributeddimid FROM... WHERE ... <условие на attrname, построенное на основе фильтра>) AND ... <условия на значения прочих измерений>

Обратите внимание на это IN. Предупреждаем и подчёркиваем, что предпочтительным способом работы с «атрибутами измерений» является их вынос в отдельные поля таблицы фактов (хоть на уровне таблицы, хоть JOIN-запросами). Использование описываемого механизма допустимо лишь в том случае, если без него никуда (а именно, если возникает недопустимое уширение таблицы фактов, или если для экономии места используется механизм "разреженных измерений").

Чтобы система знала о справочнике значений измерения, необходимо включить его в дескриптор. Делается это добавлением следующего тэга:

           <facts tablename="facts">
                .. ля-ля-ля..
                <!-- Атрибутированное измерение объявляется в таблице фактов точно так же, как всякое иное быстрое измерение, и в общем, тут ничего не меняется -->
                <dimension fieldtype="numeric">fdim3</dimension>
                .. ля-ля-ля..
            </facts>
           
            <!--Описание справочника значений атрибутированного измерения. Таких тэгов может быть несколько!!
              атрибут tablename --- это имя таблицы со справочником значений измерения... -->
            <attibuteddim dimid="fdim3" tablename="attdim">
                <!--Это поле первичного ключа -- его значения прописываются в поле fdim3 таблицы фактов-->
                <attdimvalue>id</attdimvalue>
                <!-- А это пошли поля атрибутов. Пока что поддерживаются всего два типа значений: числовой и текстовый. 
                  Весь "фарш", связанный с датами и периодами, для атрибутов отсутствует. Если нужно будет -- доработаю отдельно.-->
                <attribute>attr1</attribute>
                <attribute fieldtype="numeric">attr2</attribute>
                <attribute>attr3</attribute>
            </attibuteddim>

Фильтры

Здесь и далее фильтрами мы будем называть Python-объекты, определяющие подмножество записей в таблице фактов.

Фильтры задаются ассоциативными массивами, в которых

  • ключом записи является код измерения, для атрибутированных измерений ключ формируется в виде код:атрибут (код измерения – двоеточие – имя атрибута).
  • значением записи является ограничение на значение измерения

Ограничения по быстрым и гибким измерениям применяются наравне в одном фильтре. В качестве кода быстрого измерения используется имя поля в таблице фактов, в качестве кода гибкого измерения используется его код из справочника гибких измерений.

Ограничения на значение измерения задаются следующей формальной грамматикой:

01.filter.png

Ограничение состоит из одного или нескольких термов, перечисленных через запятую. Это приводит к запросам вида where dimvalue = … or dimvalue = … or dimvalue =... или вида where dimvalue in (…, …, …), где вместо многоточий подставляются преобразованные термы (term).

Термы могут быть следующего вида:

02.term.png

  • <value> — явно указанный код значения измерения.
  • код значения измерения со знаком процента на конце. Это ограничение приводит к условию where dimvalue LIKE <значение%> в SQL-запросе. Данная опция предназначена для расчётов по иерархическим справочникам измерений с иерархией Дьюи (например, чтобы выбрать все значения иерархии из группы 1, необходимо задать ограничение вида 1.%).
  • диапазон значений измерения (два значения кодов, разделённые двумя точками, например: 10..20). Приводит к условию where dimvalue <= значение1 and dimvalue >= <значение2>. Допустимо не указывать начало или конец диапазона.
  • Про опциональный знак «минус» перед термом см. информацию ниже.

Для измерений типа «дата» действуют следующие правила: в качестве явно указанного кода значения измерения можно указать

  • дату в формате yyyymmdd (выфильтровываются факты только на указанную дату),
  • диапазон месяца в формате yyyyymm (приводит к условию where year (…) = yyyy and month (...) = mm),
  • диапазон квартала в формате yyyyQq (год, буква Q и номер квартала), приводит к условию where year(...) = yyyy and month(...) in (1,2,3) (если, например, q = 1)
  • диапазон года в формате yyyy, приводит к условию where year(...) = yyyy.
  • использование значения с процентом недопустимо. Явным образом диапазоны можно указывать только для полных дат, т. е. в формате yyyymmdd..yyyymmdd.
  • Для измерений типа «период» действуют следующие правила:
  • При указании явного кода значения измерения в формате yyyy, yyyymm, yyyyQq, yyyy выфильтровываются только данные, явно привязанные к этому значению (т. е. где код значения в точности совпадает с указанным).
  • Значение с процентом работает так же, как и для других измерений (например, в фильтр 2011% попадут значения 2011, 2011Q2, 20110311).

Использование знака «минус» в фильтрах для инвертирования знака значения в агрегирующих функциях

Одно из измерений можно объявить, как поддерживающее минусы в фильтрах, для этого в корневой тэг дескриптора необходимо добавить атрибут:

    <descriptor mindminusdim="ACCOUNT">

где "ACCOUNT" --- измерение, поддерживающее минуса в фильтрах. Если этот атрибут не добавить, система будет работать как обычно. Если же атрибут добавить, то выражения вида

    '202%,20302..20308,20401..20403,20317,20318,-20321'

будут автоматически интерпретироваться как: «значение в точке 2031 при агрегировании будет учитываться с инвертированным знаком». Корректно поддерживаются все агрегирующие функции (SUM, MIN, MAX, на агрегирующую функцию COUNT знаки «минус» влияния не оказывают).

Знак «минус» поддерживаются в любых фильтрах: так, «-202%» будет означать инвертирование знака во всех точках «like 202%», а «-20302..20308» — во всех точках «between 20302 and 20308» (обратите внимание, что во втором примере минус надо ставить только перед первым значением).

Описание иерархии показателей в виде классов

Система использует описание иерархии базовых и расчётных показателей в виде классов, построенных по определённым правилам. Диаграмма классов получающейся системы представлена на рис. 3

Calc pic3.png

Рисунок 3: Классы библиотечного и кастомизируемого модуля системы расчётов показателей.

Модуль calculation

Модуль calculation содержит процедуру уровня модуля setDBType(dbtype), принимающую одно из следующих значений:

  • 'MSSQL' — работа с базой данных MS SQL Server
  • 'POSTGRES' — работа с базой данных PostgreSQL
  • 'ORACLE' — работа с базой данных Oracle

Корневой класс Calculation

Данный класс является основным контекстом сеанса вычислений. Именно он инстанцирует (по мере необходимости) классы, описывающие значения показателей, запускает нужные методы, чтобы выполнить расчёт, поддерживает внутреннее состояние показателей, благодаря которому избегаются ненужные повторные перерасчёты.

Экземпляры наследников класса BaseFigure (т. е. все экземпляры классов, описывающих показатели) инициируются в контексте объекта типа Calculation, который мы будем также называть «текущим сеансом вычислений».

Пользователь системы самостоятельно инстанцирует экземпляр этого класса, передавая в качестве параметров:

  • соединение с базой данных, ВНИМАНИЕ Это должно быть Python-соединение (zxJDBC), при этом Celesta передаёт в ваше распоряжение Java-соединение (JDBC). Сконвертировать Java-соединение в zxJDBC просто: если conn — Java-соединение, то:
from com.ziclix.python.sql import PyConnection
pyconn = PyConnection(conn)
  • дескриптор базы данных (XML-документ, описанный выше),
  • набор кодов измерений, по которым будет осуществляться полное итерирование при вычислениях. Чем больше этот набор, тем больше детализация при расчёте показателей. В целях экономии времени и практических соображений, расчётные показатели часто имеет смысл вычислять лишь в детализации по некоторому подмножеству измерений (к примеру, «организация» и «дата»),
  • глобальный фильтр, выделяющий подмножество исходных данных, по которому производится расчёт (к примеру, данные только за определённый год или только по определённой бизнес-группе).

Чтобы инициировать расчёт по какому-либо расчётному показателю, необходимо проинстанцировать его с помощью метода getFigure(figureClass), передав класс интересующего нас показателя в качестве параметра, а затем выполнить метод calculate() (практический пример кода следует ниже).

Базовый Класс BaseFigure базового показателя

От данного класса наследуются все классы, описывающие базовые (не нуждающиеся в расчёте, а содержащиеся в системе в готовом виде) показатели. В нём определены основные методы получения агрегированных значений по фильтру (sum, max, min, count), он является также предком базового класса для вычисляемых показателей.

В классе реализованы следующие методы:

  • getFigureId() — возвращает код показателя, описываемого классом. Этот код определяется следующим образом: если в классе инициализирован статический атрибут figureId, то используется его значение. В противном случае используется имя класса. Представляется хорошей практикой выбирать такие коды для показателей, чтобы их можно было использовать напрямую в качестве имён классов без того, чтобы придумывать имена классов отдельно от кодов показателей.
  • getDescription() — возвращает описание показателя. Для ввода описания показателя используется Python-документация класса, описывающего показатель.
  • sum(filter), max(filter), min(filter), count(filter) — возвращает агрегированное значение по данному показателю с учётом передаваемого в метод фильтра по измерениям. Вычисление осуществляется путём передачи SQL-запроса с соответствующей агрегирующей функцией в базу данных.

Базовый класс CalculatedFigure вычисляемых показателей

Класс унаследован от BaseFigure. В его экземплярах содержится boolean-атрибут calculated, по умолчанию установленный в False. Значение False данного атрибута интерпретируется следующим образом: база данных ещё не заполнена актуальными значениями по данному показателю, их следует вычислить и записать в таблицу фактов прежде, чем по показателю могут быть вычислены агрегаты sum, max и т. п.

Помимо унаследованных от BaseFigure, в данном классе определены (переопределены) следующие методы:

  • sum(filter), max(filter), min(filter), count(filter) — возвращают такие же агрегированные значения, как и в классе BaseFigure, однако имеют дополнительную функциональность: если на момент вызова любого из этих методов оказывается, что показатель ещё не перерасчитан — выполняется перерасчёт, и лишь затем тем же самым механизмом SQL-запроса выбирается агрегированное значение.
  • getFigure(figureClass) — получает экземпляр класса-показателя, от значений которого зависит данный показатель. Чтобы этот экземпляр был правильно проинстанцирован и правильно функционировал в контексте текущего сеанса вычислений, следует использовать только данный метод, а не конструкторы классов-показателей.
  • calculate() — метод без параметров, инициирующий перерасчёт данного показателя в таблице фактов, только если в рамках текущего сеанса вычислений перерасчёт не был уже выполнен. Перерасчёт выполняется по набору измерений и в рамках фильтров, указанных в текущем сеансе вычислений (т. е. экземпляре класса Calculation). Метод работает следующим образом:
    • сначала вызывается protected-метод _beforeCalculate().
    • затем, руководствуясь параметром dimensions текущего объекта Calculation, определяется набор всех возможных комбинаций интересующего нас набора измерений. Метод итерирует по каждой из этих комбинаций и вызывает абстрактный protected-метод _calculate(context), где в качестве context передаётся фильтр, соответствующий текущей комбинации значений измерений.
  • _beforeCalculate() — protected-метод, реализация на уровне классе CalculatedFigure он не делает ничего. Классы-наследники могут переопределить данный метод для инициализации переменных, которые будут общими для всего цикла вычислений (например, определения некоторого суммарного количества для того, чтобы нормировать показатель на это количество в каждом отдельном случае при вызове метода _calculate).
  • _calculate(context) — абстрактный protected-метод, реализация на уровне класса CalculatedFigure выбрасывает исключение NotImplementedError. Таким образом, классы-наследники обязаны переопределять этот метод. Собственно, в реализации данного метода и должны содержаться формулы для вычисления значения данного показателя в данном контексте. Владея контекстом и экземплярами других расчётных и базовых показателей, получаемыми через метод getFigure(), мы можем получать агрегаты по другим показателям и из них получать значение текущего показателя в указанном контексте. При этом система сама обеспечивает правильный порядок перерасчёта и не перерасчитывает показатели лишний раз. Возвращаемый результат записывается в таблицу фактов.

Пример кода кастомизированного модуля

Приводится пример кастомизированного модуля, содержащего описания двух базовых и трёх расчётных показателей.

# coding=UTF-8
"""
Пример модуля, содержащего описание вычисляемых показателей
"""
import calculation

# Базовые показатели
class BFig1(calculation.BaseFigure):
    """
    Здесь должно быть длинное описание первого показателя. Если код показателя 
    в измерении 'показатель' совпадает с именем класса, то для базовых показателей больше ничего определять не нужно.
    """
    pass

class BFig2(calculation.BaseFigure):
    """
    Если код показателя в измерении 'показатель' не совпадает с именем класса, то определяем  статический атрибут figureId.
    """
    figureId = 'BBB'
    
# Вычисляемые показатели
class Fig1(calculation.CalculatedFigure):
    """Таким же образом задаются описания и атрибуты FigureId. Кроме того, вычисляемый показатель  обязан переопределить метод _calculate"""
    def _calculate(self, context):
        f = self.getFigure(BFig1)
        return f.sum(context) + 1
    
class Fig2(calculation.CalculatedFigure):
    """Вычисляемый показатель второй, длинное русское описание"""
    figureId = 'FIG2ID'
    def _calculate(self, context):
        f = self.getFigure(BFig2)
        return f.sum(context) + 1
    
class Fig3(calculation.CalculatedFigure):
    """Вычисляемый показатель третий, длинное русское описание"""
    
    def _beforeCalculate(self):
        """Здесь можно проинстанцировать что-то нетривиальное, что,
        будучи вычислено один раз, потом будет использоваться в каждом
        из многочисленных вызовов _calculate"""
        self.f1 = self.getFigure(Fig1)
        self.f2 = self.getFigure(Fig2)
    
    def _calculate(self, context):
        return self.f1.sum(context) + self.f2.sum(context) + self.f1.sum(context)

Пример кода запуска расчёта

# Процесс инициирования вычисления выглядит так: сначала инстанцируем объект
# Calculation, передавая в параметрах в него соединение с БД, дескриптор БД, 
# нужные нам набор измерений и фильтров
c = calculation.Calculation(conn, descr, ['fdim1', 'fdim2'], {})

# Затем выбираем тот показатель, который нам надо вычислить в рамках данного 
# сеанса вычислений, и вызываем метод calculate(). При этом пересчитывается всё то, 
# от чего данный показатель зависит. 
c.getFigure(Fig3).calculate()

# Мы можем проделать вычисление и для другого показателя, при этом, пока цел 
# объект Calculation, пересчёты уже вычисленных показателей производиться не 
# будут. В частности, т. к. Fig2 уже был расчитан, в контексте объекта c 
# вызов данного метода ни к чему не приведёт (и не займёт времени).
c.getFigure(Fig2).calculate()

Отладка решения

Как всякий Python-скрипт, решение можно отлаживать пошагово в Eclipse. Однако из-за обилия вложенных циклов делать это на реальных данных бывает затруднительно, и самым большим вопросом является зачастую: «Почему значение агрегирующей функции равно именно этому?» Для отладки агрегирующих функций предусмотрен вывод отладочной информации в консоль.

Если при вызове расчётного метода-агрегатора вторым аргументом заслать True — например, так:

class Fig2(calculation.CalculatedFigure):
    def _calculate(self, context):
        f = self.getFigure(BFig2)
        return f.sum(context, True) + 1

то при вычислении в консоль будет выводиться отладочная информация в виде:

---FIGURE BBB AGGRREGATION DEBUG INFO---
dimfilter: {'fdim1': u'b', 'fdim2': u'b'}
SQL to get debug data: select * from facts where facts.figureId = 'BBB' and facts.fdim1 = 'b' and facts.fdim2 = 'b';
 4|       BBB| None| None| None| b| b| 2.0
 8|       BBB| None| None| None| b| b| 2.0
And aggregated value is 4.0
--------------------------------------- 

В консоли находится SQL-запрос, возвращающий данные, от которого берётся агрегат, сами данные в текстовом формате и итоговое значение агрегата.