Перейти к содержанию

BI (Business Intelligence)#

Пример отображения

BI (Business Intelligence) - система для обработки, анализа и наглядного представления данных.

Требования#

  • Устрановите библиотеку ecosoft-lexema8-bi последней версии.
  • Собрерите образ cubejs. Для интеграции с лексемой необходимо при сборке образа добавить модель model/cubes/cube.js
model/cubes/cube.js
const fetch = require("node-fetch");

asyncModule(async () => {
  let dynamicCubes = await (
    await fetch("http://host:port/cube/models") // host - имя хоста, где развернут сервис data, port - порт сервиса data
  ).json()

  const convertStringPropToFunction = (propNames, dimensionDefinition) => {
    let newResult = { ...dimensionDefinition };
    propNames.forEach((propName) => {
      const propValue = newResult[propName];

      if (!propValue) {
        return;
      }

      newResult[propName] = () => propValue;
    });
    return newResult;
  };

  const transformDimensions = (dimensions) => {
    return Object.keys(dimensions).reduce((result, dimensionName) => {
      const dimensionDefinition = dimensions[dimensionName];
      return {
        ...result,
        [dimensionName]: convertStringPropToFunction(
          ["sql"],
          dimensionDefinition
        ),
      };
    }, {});
  };

  const transformMeasures = (measures) => {
    return Object.keys(measures).reduce((result, dimensionName) => {
      const dimensionDefinition = measures[dimensionName];
      return {
        ...result,
        [dimensionName]: convertStringPropToFunction(
          ["sql", "drill_members"],
          dimensionDefinition
        ),
      };
    }, {});
  };

  dynamicCubes.forEach((dynamicCube) => { 
    cube(dynamicCube.Name, Object.assign({}, dynamicCube.Settings, {
        sql_table: dynamicCube.Settings.sql_table,    
        dimensions: transformDimensions(dynamicCube.Settings.dimensions),
        measures: transformMeasures(dynamicCube.Settings.measures)
    }))
  });
});

В конфигурационнм файле добавьте настройку schemaVersion

Конфигурационный файл
1
2
3
4
5
6
7
module.exports = {
  schemaVersion: async ({ securityContext }) => {
    const response = await fetch('http://host:port/cube/version') // host - имя хоста, где развернут сервис data, port - порт сервиса data
    const schemaVersion = await response.text()
    return schemaVersion
  }
}
  • Запустите настроенный докер образ cubejs
  • В конфигурационном файле в разделе applications.data.cube настройте подключение к cubejs.

Подключение форм#

Пример файла units.json

[{
    "caption": "demo",
    "name": "demo",
    "roles": [
      "super"
    ],
    "views": [
      {
        "key": "cubeRegistry",
        "caption": "cubeRegistry",
        "form": ["CubeRegistry", "ecosoft-lexema8"]
      }, {
        "key": "cubeForm",
        "form": ["CubeForm", "ecosoft-lexema8"]
      }, {
        "key": "BIRegistry",
        "caption": "BIRegistry",
        "form": ["BIRegistry", "ecosoft-lexema8-bi"]
      }, {
        "key": "BIForm",
        "form": ["BIForm", "ecosoft-lexema8-bi"]
      }      
    ]
  }]

  • CubeRegistry и CubeForm - формы для создания кубов.
  • BIRegistry и BIForm - формы для создания аналитических форм.

CubeForm#

Пример отображения

В реестре CubeRegistry создайте новый документ.

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

В поле описание необходимо добавить описание куба в формате json (см. документацию cubejs).

Пример описания куба
{
  "sql_table": "lex.\"Log\"",
  "data_source": "default",
  "joins": {},
  "dimensions": {
    "id": {
      "sql": "\"Id\"",
      "type": "number",
      "primary_key": true
    },
    "chost": {
      "sql": "\"CHost\"",
      "type": "string"
    },
    "usermessage": {
      "sql": "\"UserMessage\"",
      "type": "string"
    },
    "service": {
      "sql": "\"Service\"",
      "type": "string"
    },
    "cuser": {
      "sql": "\"CUser\"",
      "type": "string"
    },
    "details": {
      "sql": "\"Details\"",
      "type": "string"
    },
    "level": {
      "sql": "\"Level\"",
      "type": "string"
    },
    "request": {
      "sql": "\"Request\"",
      "type": "string"
    },
    "message": {
      "sql": "\"Message\"",
      "type": "string"
    },
    "whost": {
      "sql": "\"WHost\"",
      "type": "string"
    },
    "wuser": {
      "sql": "\"WUser\"",
      "type": "string"
    },
    "code": {
      "sql": "\"Code\"",
      "type": "string"
    },
    "wdate": {
      "sql": "\"WDate\"",
      "type": "time"
    },
    "cdate": {
      "sql": "\"CDate\"",
      "type": "time"
    }
  },
  "measures": {
    "count": {
      "type": "count"
    }
  },
  "pre_aggregations": {}
}

BIForm#

Пример отображения

В реестре BIRegistry создайте новый документ.

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

На тулбаре нажмите кнопку Режим редактирования.

В левой части документа отображается список доступных контролов. Перетащите контролы в рабочую область документа. в Настройках контрола задайте куб, измерения, меры. Сохраните документ.

Встраивание BIForm на стартовую форму.#

Пример стартовой формы
1
2
3
4
5
6
7
8
9
<Form writeRoles="super" title="start">
  <Layout>
    <Group width="100%" editorWidth="12">
      <Widget width="100%" height="500px" editorWidth="6" form="./demo"
        caption="demo" showExpandButton="false"
      />
    </Group>
  </Layout>
</Form>
1
2
3
4
5
6
7
8
import { Form } from 'ecosoft-lexema8'
import template from './start.xml'

export default class Start extends Form {
  static get template () {
    return template
  }
}
Пример аналитической формы (BI)
1
2
3
4
5
<Form writeRoles='super' title="BIForm demo">
  <Layout>
    <NestedForm editorWidth="12" id="nested" height="500px"/>
  </Layout>
</Form>
import { BIForm } from 'ecosoft-lexema8-bi'
import { Form, QuerySource } from 'ecosoft-lexema8'
import template from './demo.xml'
import Query from './demo.query.json'
import SimpleForm from './simple'

export default class Demo extends Form {
  constructor (args) {
    super(args)
    const source = new QuerySource(Query)
    source.load().done((data) => {
      const id = data.length > 0 ? data[0].VCode : null
      let DemoForm = SimpleForm
      let formParams      

      if (id) {
        formParams = { id }
        DemoForm = BIForm
      }

      this.create({
        FormClass: DemoForm,
        nestedForm: this.controls.nested,
        formParams
      })  
    })
  }

  static get template () {
    return template
  }
}
1
2
3
4
5
6
7
8
9
{
  "select": {
    "text": "./demo.sql",
    "parameters": []
  },
  "rights": {
    "read": ["super"]
  }
}
declare @userName varchar (max) = dbo.[getLogin]()
declare @orgId bigint = dbo.[GetOrgId]()

;with roles (id) as
(
  select  our.[Role]
  from [lex].[OrganizationUser] ou
    inner join [lex].[OrganizationUserRole] our on ou.[Id] = our.[OrganizationUser]
  where  ou.[User] = (select Id from [lex].[UserMeta] where UserName = @userName)
        and ou.[Organization] = @orgId
  union all
  select    nr.[Role]
  from roles r
    inner join [lex].[NestedRole] nr on r.[Id] = nr.[Parent]
)

select top 1 * from bi.BI b 
where exists (select 1 from roles r where r.id = b.[Role])