Схемы расчетов

В данном разделе представлен обзор структуры, принципов работы и процесса создания схем расчетов в системе. Управление схемами расчетов в системе осуществляется в разделе Экспертиза.

О схемах расчетов

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

Расчеты в схемах поддерживают наследование — возможность создавать расчеты, копирующие свойства наследуемых расчетов за исключением тех, которые явно переопределены.

Результатом расчета по схеме всегда является конечное число.

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

  1. Создать схему расчетов в разделе Экспертиза.

  2. Определить структуру схемы расчетов.

  3. Настроить запуск расчетов по схеме и отображение их результатов в интерфейсе.

Работа со схемой расчетов

Структура схемы расчетов

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

Поле Описание Тип данных Обязательное поле

id

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

строка

да

name

Название схемы расчетов. Допустимы латинские буквы, цифры, подчеркивания.

строка

да

description

Краткое описание назначения схемы расчетов.

строка

нет

type

Тип элемента экспертизы. Всегда устанавливается значение calculation_schema.

перечисление

да

tags

Теги для классификации и поиска схемы в системе.

массив строк

нет

version

Версия схемы в формате Semantic Versioning.

строка

да

status

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

Примеры значений:

  • debug — версия в разработке;

  • experimental — экспериментальная версия;

  • test — версия для тестирования;

  • stable — стабильная версия.

строка

нет

author

Имя и контактные данные автора схемы.

строка

нет

extends

FQID схемы расчетов, в которой определяются расчеты-наследники для расчетов из текущей схемы.

строка

нет

calculations

Массив расчетов. Каждый расчет включает FQID сущностей, к которым применяются расчеты, а также формулы или ссылки на скрипты, которые определяют логику расчетов. См. описание поля.

массив объектов

нет

summary_calculations

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

массив объектов

нет

В схеме расчетов должен быть определен хотя бы один расчет в поле calculations или summary_calculations.

Расчеты: поле calculations

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

Результатом расчета по схеме всегда является конечное число.

Объект расчета имеет следующую структуру:

Поле Описание Тип данных Обязательное поле

id

Уникальный идентификатор расчета, по которому можно сослаться на расчет. Должен быть уникальным в пределах схемы расчетов. Можно использовать UUID в качестве значения.

строка

да

name

Имя расчета, отображаемое в интерфейсе.

строка

да

description

Краткое описание назначения расчета.

строка

нет

entity

FQID сущности, к которой применяется расчет.

строка

да

expression

Математическая формула, по которой выполняется расчет. См. правила описания формулы.

строка

да

complexity_limit

Максимальная сложность расчета.

Сложность рассчитывается следующим образом:

  • базовая сложность выражения в поле expression — 1;

  • каждый скрипт, входящий в выражение, увеличивает его сложность на 10;

  • к сложности родительского расчета прибавляется сложность всех дочерних расчетов.

При превышении предела сложности расчета, указанной в поле complexity_limit, расчет завершится с ошибкой.

Необязательно точно рассчитывать complexity_limit для расчета; можно указать достаточно большое число, например, 100. Если этот предел будет превышен, возникшая ошибка укажет на то, что расчет следует оптимизировать.

положительное целое число

да

auto_recalculation

Будет ли расчет автоматически запущен при обновлении свойств или связей объекта, связанного с ним.

булево

да

children

Дочерние расчеты.

массив объектов

нет

Правила описания формулы расчета

В поле expression следует указать формулу для расчета согласно следующим правилам:

  • В формулах поддерживаются все операции, реализованные в библиотеке JavaScript math.js.

  • В формулах можно использовать следующие аргументы:

    • целые и дробные числовые константы;

    • все атрибуты сущности, указанной в поле entity расчета, имеющие типы Integer, Float;

    • глобальные переменные типов Integer, Float;

    • скрипты;

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

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

Пример формулы
- id: test_calc
  name: Тестовый расчет
  entity: test_domain/TestEntity
  expression: "2 * {field:weight} / {variable:divisor}"
  auto_recalculation: false
  complexity_limit: 1

Здесь:

  • 2 — числовая константа.

  • {field:weight} — значение поля weight сущности TestEntity.

  • {variable:divisor} — значение глобальной переменной divisor.

Ссылки на атрибуты, переменные и скрипты следует указывать в фигурных скобках, например, {variable:divisor}.

Применение скриптов в расчетах

В поле expression можно указать идентификатор скрипта, который будет выполнен при запуске расчета. В расчете будет использоваться значение, возвращенное скриптом.

Результатом работы скрипта должно быть конечное число. Автоматическая валидация возвращаемого значения в системе не предусмотрена, поэтому разработчик скрипта должен реализовать его так, чтобы он возвращал только числа.
Пример формулы, дополненный вызовом скрипта
- id: test_calc
  name: Тестовый расчет
  entity: test_domain/TestEntity
  expression: |
    mean(
      2 * {field:weight} / {variable:divisor},
      {script:test_calculation_script}
    )
  auto_recalculation: false
  complexity_limit: 11

Здесь:

  • {script:test_calculation_script} — вызов скрипта test_calculation_script.

Минимально допустимое значение complexity_limit для данного расчета — 11, поскольку к базовой сложности расчета 1 следует прибавить 10 — сложность вызова скрипта.

Дочерние расчеты

Дочерние расчеты — это расчеты для связанных сущностей, участвующие в расчете для текущей сущности (родительском расчете). Ссылки на них следует указать в поле children родительского расчета.

Ссылка на дочерний расчет содержит следующие поля:

  • linkage — идентификатор связи между сущностью, для которой выполняется родительский расчет, и сущностью, для которой выполняется дочерний расчет. Связь должна быть настроена в схеме домена в поле linkages описания сущности, для которой выполняется родительский расчет.

  • calculation — идентификатор дочернего расчета.

Дочерний расчет выполняется для всех сущностей, связанных с текущей, поэтому его результат всегда интерпретируется системой как массив. Чтобы преобразовать результат дочернего расчета в одно число, используйте функции массивов, такие как min, max, mean, count или sum.
Пример настройки дочернего расчета
- id: test_calc_with_child
  name: Тестовый расчет с дочерним
  entity: test_domain/TestEntity
  expression: |
    mean(
      2 * {field:weight},
      sum({calculation:child_calc})
    )
  children:
    - linkage: test_domain/TestEntity_ChildEntity
      calculation: child_calc
  auto_recalculation: true
  complexity_limit: 200

- id: child_calc
  name: Дочерний расчет
  entity: test_domain/ChildEntity
  expression: "{script:child_calculation_script}"
  auto_recalculation: true
  complexity_limit: 100

Здесь:

  • Результат дочернего расчета преобразован в одно число с помощью функции sum: sum({calculation:child_calc}).

  • test_domain/TestEntity_ChildEntity — идентификатор связи между сущностями TestEntity и ChildEntity, заданной в схеме домена test_domain в разделе linkages описания сущности TestEntity.

  • child_calc — идентификатор дочернего расчета.

Обобщающие расчеты: поле summary_calculations

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

Результатом расчета по схеме всегда является конечное число.

В схеме расчетов обобщающие расчеты настраиваются в поле summary_calculations.

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

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

Обобщающий расчет имеет следующую структуру:

Поле Описание Тип данных Обязательное поле

id

Уникальный идентификатор расчета, по которому можно сослаться на расчет. Должен быть уникальным в пределах схемы расчетов. Можно использовать UUID в качестве значения.

строка

да

name

Имя расчета, отображаемое в интерфейсе.

строка

да

description

Краткое описание назначения расчета.

строка

нет

expression

Математическая формула для обобщающего расчета.

строка

да

Пример обобщающего расчета
calculations:
  - id: task_processing_time
    name: Время обработки задачи
    entity: test_tasks/Task
    expression: "{field:processing_time}"
    auto_recalculation: true
    complexity_limit: 1

summary_calculations:
  - id: mean_task_processing_time
    name: Среднее время обработки задачи
    expression: "mean({calculation:task_processing_time})"

Здесь:

  • task_processing_time — расчет, возвращающий время выполнения одной задачи.

  • mean_task_processing_time — обобщающий расчет, возвращающий среднее время выполнения всех задач в системе.

Пример схемы расчетов

В данном разделе приведен пример схемы расчетов.

id: evo.compliance.audit.calculations
name: audit_calculations
description: Расчеты для аудитов
type: calculation_schema
tags: [audit, calculations]
version: 1.0.0
status: stable
author: John Doe <johndoe@example.com>
calculations:
  - id: audit_assessment_sum_compliance_index
    name: Сумма индексов соответствия
    entity: evo.compliance.audit/Audit
    expression: |
      round(
        sum({calculation:audit_assessment_compliance_index}),
        {variable:audit_assessment_compliance_index_round}
      )
    children:
      - linkage: evo.compliance.audit/Audits_RequirementValues
        calculation: audit_assessment_compliance_index
    auto_recalculation: true
    complexity_limit: 100

  - id: audit_assessment_compliance_index
    name: Индекс соответствия, вычисленный через скрипт
    entity: evo.compliance.audit/RequirementValues
    expression: "{script:calculate_comp_index}"
    auto_recalculation: true
    complexity_limit: 20

  - id: progress_metric
    name: Прогресс аудита в процентах
    entity: evo.compliance.audit/Audit
    expression: "{field:progress}"
    auto_recalculation: true
    complexity_limit: 1

summary_calculations:
  - id: mean_audit_progress
    name: Средний прогресс по аудиту
    expression: "mean({calculation:progress_metric})"

Интерпретация примера

Метаданные схемы расчетов

  • id: идентификатор схемы расчетов — evo.compliance.audit.calculations.

  • name: наименование схемы расчетов — audit_calculations.

  • description: описание схемы расчетов — "Расчеты для аудитов".

  • type: тип элемента экспертизы — calculation_schema.

  • tags: теги схемы расчетов, используемые для классификации — audit, calculations.

  • version: версия схемы расчетов — 1.0.0.

  • status: статус разработки схемы — stable.

  • author: автор схемы расчетов — John Doe <johndoe@example.com>.

Расчеты

Схема содержит три расчета:

  • audit_assessment_sum_compliance_index — сумма индексов соответствия требований аудита, округленная до количества разрядов, определенного глобальной переменной audit_assessment_compliance_index_round. Индексы соответствия отдельных требований вычисляются с помощью дочернего расчета audit_assessment_compliance_index.

  • audit_assessment_compliance_index — индекс соответствия требования, вычисляемый скриптом calculate_comp_index.

  • progress_metric — расчет, возвращающий значение поля прогресса аудита progress для обобщающего расчета mean_audit_progress.

Обобщающие расчеты

Схема содержит обобщающий расчет mean_audit_progress, вычисляющий средний прогресс по аудитам в системе с помощью расчета progress_metric.

Запуск и отображение расчетов по схеме

Расчеты можно запускать:

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

  1. Если расчеты будут запускаться при нажатии кнопки или по триггеру, следует создать плейбук для запуска расчетов.

    Если схема расчетов выключена в разделе Экспертиза, при запуске связанного с ней плейбука любым способом расчеты по ней производиться не будут.
  2. Настроить метод запуска плейбука расчетов.

Создание плейбука для запуска расчетов

Плейбук для запуска расчетов по схеме должен использовать SDK calculate.

Пример плейбука для запуска расчета
id: calculation_sdk_test_exec
name: Вызов exec через SDK расчётов
version: 1.0.0
author: John Doe <johndoe@example.com>
tags: [audit, calculations]
type: playbook
body:
  input:
    properties: {}
  modules:
    - id: module_0
      body:
        type: rawscript
        input: {}
        source: !bun |
          import * as sdk from '@playbooks/sdk';
          export async function main() {
            // Получение ID текущего объекта из контекста.
            const objectId = await JSON.parse(get_context()).objectId;

            // Формирование объекта с аргументами расчета.
            const command = {
              calculationSchemaId: "calculate_test",
              calculationId: "test",
              object: {
                domainId: "evo.entity.category.standards",
                entityId: "Requirement",
                id: objectId
              },
              cacheFirst: false
            }

            // Выполнение расчета и получение его результатов.
            const result = await sdk.calculation.exec(command);
            console.log('result', result);
            return result;
          }

Настройка метода запуска расчетов

Запуск расчетов при нажатии кнопки

Чтобы запустить расчеты при нажатии кнопки, следует внести изменения в схему домена:

  1. создать действие типа playbook, в котором указать идентификатор ранее созданного плейбука;

  2. создать кнопку (виджет типа button) и связать ее с действием;

  3. добавить кнопку в представление (например, таблицу или карточку) сущности, для которой производится расчет.

Пример описания кнопки для запуска расчетов
actions:
  - id: actions.countComplianceIndex
    name: Вычислить индекс соответствия
    type: playbook
    entity: Audit
    playbook: count_compliance_index

views:
  - id: auditView
    type: entity
    entity: Audit

    widgets:
      - id: countComplianceIndexButton
        type: action
        action: actions.countComplianceIndex
        control:
          type: regular_button
          label: "Вычислить индекс соответствия"

    groups:
      - id: infoAuditBlock
        description: Информация об аудите
        type: block
        layout:
          direction: column
          expandable: false
        components:
          - widget: countComplianceIndexButton

Интерпретация примера

  1. Действие actions.countComplianceIndex связано с плейбуком count_compliance_index, который запускает схему расчета индекса соответствия для аудита.

  2. Кнопка countComplianceIndexButton запускает действие actions.countComplianceIndex.

  3. Представление карточки аудита infoAuditBlock содержит кнопку countComplianceIndexButton.

Запуск расчетов с помощью триггера

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

Запуск расчетов при изменении связанного объекта

Если в конфигурации расчета установить для свойства auto_recalculation значение true, при изменении свойств или связей объекта, связанного с расчетом, расчет будет выполнен заново.

Для этого метода запуска создавать плейбук не требуется.

Необходимость перезапуска родительских и дочерних расчетов относительно текущего расчета определяется по следующей логике:

  • При перезапуске дочернего расчета родительские расчеты, использующие его результаты, будут выполнены заново.

    Например, расчет для требования — дочерний для расчета по группе требований, а расчет по группе требований — дочерний для расчета по комплексу требований. Тогда при изменении свойств требования будут выполнены заново расчеты для этого требования, а также для группы и комплекса требований, в которые оно входит.

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

    Например, при перезапуске расчета по группе требований будут перезапущены расчеты только для тех требований, свойства которых изменились с прошлого запуска расчета по группе. Результаты остальных расчетов будут получены из системного хранилища результатов расчетов.

Отображение результатов расчетов в интерфейсе

Чтобы отобразить результат расчетов в интерфейсе системы, нужно связать расчет с атрибутом сущности с помощью поля calculation, а затем добавить виджет для вывода атрибута в представление сущности.

Пример вывода результата расчета в карточку требования
entities:
  - id: Requirement
    attributes:
      - id: assessmentIndexCalculation
        name: Результат расчета индекса соответствия
        dataType: Calculation
        calculation:
          schemaId: evo.compliance.audit.calculations
          id: audit_assessment_compliance_index

...

views:
  - id: requirementView
    type: entity
    entity: Requirement
    widgets:
      - id: editors.assessmentIndexCalculation
        type: attribute
        attribute: assessmentIndexCalculation
        description: Результат расчета индекса соответствия

    groups:
      - id: blocks.info.requirement
        description: Информация о требовании
        type: block
        layout:
          direction: column
          expandable: false
        components:
          - widget: editors.assessmentIndexCalculation