# Value Stream Analytics development guide

> 原文：[https://docs.gitlab.com/ee/development/value_stream_analytics.html](https://docs.gitlab.com/ee/development/value_stream_analytics.html)

*   [Stage](#stage)
    *   [Events](#events)
        *   [Implementing an `Event` class](#implementing-an-event-class)
    *   [Validating start and end events](#validating-start-and-end-events)
    *   [Parent](#parent)
        *   [How parent relationship it work](#how-parent-relationship-it-work)
    *   [Default stages](#default-stages)
*   [Data Collector](#data-collector)
*   [Database query](#database-query)
*   [High-level overview](#high-level-overview)
*   [Testing](#testing)

# Value Stream Analytics development guide[](#value-stream-analytics-development-guide "Permalink")

值流分析计算域对象上记录的两个任意事件之间的时间，并提供有关持续时间的汇总统计信息.

有关如何在 GitLab 中配置 Value Stream Analytics 的信息，请参阅我们的[分析文档](../user/analytics/value_stream_analytics.html) .

## Stage[](#stage "Permalink")

在开发过程中，会发生一些事件，这些事件会移动问题并在不同的进展阶段合并请求，直到认为它们完成为止. 这些阶段可以用`Stage`模型表示.

示例阶段：

*   Name: Development
*   开始事件：问题已创建
*   结束事件：在提交中首先提到的问题
*   Parent: `Group: gitlab-org`

### Events[](#events "Permalink")

事件是价值流分析功能的最小构建块. 一个阶段包括两个事件：

*   Start
*   End

这些事件在持续时间计算中起关键作用.

Formula: `duration = end_event_time - start_event_time`

为了使持续时间计算更加灵活，每个`Event`都实现为一个单独的类. 他们负责定义将在计算查询中使用的时间戳表达式.

#### Implementing an `Event` class[](#implementing-an-event-class "Permalink")

有一些方法需要实现， `StageEvent`基类非常详细地描述了它们. 最重要的是：

*   `object_type`
*   `timestamp_projection`

`object_type`方法定义将查询哪个域对象以进行计算. 当前允许两种模型：

*   `Issue`
*   `MergeRequest`

对于持续时间计算，将使用`timestamp_projection`方法.

```
def timestamp_projection
  # your timestamp expression comes here
end

# event will use the issue creation time in the duration calculation
def timestamp_projection
  Issue.arel_table[:created_at]
end 
```

**注意：**也可以使用更复杂的表达式（例如，使用`COALESCE` ）. 查看现有事件类作为示例.

在某些情况下，仅定义`timestamp_projection`方法是不够的. 计算查询应该知道哪个表包含时间戳表达式. 每个`Event`类负责修改计算查询，以使`timestamp_projection`起作用. 这通常意味着加入一个附加表.

连接`issue_metrics`表并使用`first_mentioned_in_commit_at`列作为时间戳表达式的`first_mentioned_in_commit_at` ：

```
def object_type
  Issue
end

def timestamp_projection
  IssueMetrics.arel_table[:first_mentioned_in_commit_at]
end

def apply_query_customization(query)
  # in this case the query attribute will be based on the Issue model: `Issue.where(...)`
  query.joins(:metrics)
end 
```

### Validating start and end events[](#validating-start-and-end-events "Permalink")

某些开始/结束事件对彼此不"兼容". 例如：

*   从"已创建问题"到"已合并请求"：事件类在不同的域模型上定义， `object_type`方法不同.
*   "问题已关闭"到"问题已创建"：必须先创建问题，然后才能将其关闭.
*   从"问题已关闭"到"问题已关闭"：持续时间始终为 0.

`StageEvents`模块描述了允许的`start_event`和`end_event`配对（ `PAIRING_RULES`常数）. 如果添加了新事件，则需要在该模块中注册它. 要添加新事件：

1.  在`ENUM_MAPPING`添加一个具有唯一编号的条目，该条目将在`Stage`模型中用作`enum` .
2.  在`PAIRING_RULES`哈希中定义哪些事件与该事件兼容.

支持的开始/结束事件配对：

图 LR; IssueCreated-> IssueClosed; IssueCreated-> IssueFirstAddedToBoard; IssueCreated-> IssueFirstAssociatedWithMilestone; IssueCreated-> IssueFirstMentionedInCommit; IssueCreated-> IssueLastEdited; IssueCreated-> IssueLabelAdded; IssueCreated-> IssueLabelRemoved; MergeRequestCreated-> MergeRequestMerged; MergeRequestCreated-> MergeRequestClosed; MergeRequestCreated-> MergeRequestFirstDeployedToProduction; MergeRequestCreated-> MergeRequestLastBuildStarted; MergeRequestCreated-> MergeRequestLastBuildFinished; MergeRequestCreated-> MergeRequestLastEdited; MergeRequestCreated-> MergeRequestLabelAdded; MergeRequestCreated-> MergeRequestLabelRemoved; MergeRequestLastBuildStarted-> MergeRequestLastBuildFinished; MergeRequestLastBuildStarted-> MergeRequestClosed; MergeRequestLastBuildStarted-> MergeRequestFirstDeployedToProduction; MergeRequestLastBuildStarted-> MergeRequestLastEdited; MergeRequestLastBuildStarted-> MergeRequestMerged; MergeRequestLastBuildStarted-> MergeRequestLabelAdded; MergeRequestLastBuildStarted-> MergeRequestLabelRemoved; MergeRequestMerged-> MergeRequestFirstDeployedToProduction; MergeRequestMerged-> MergeRequestClosed; MergeRequestMerged-> MergeRequestFirstDeployedToProduction; MergeRequestMerged-> MergeRequestLastEdited; MergeRequestMerged-> MergeRequestLabelAdded; MergeRequestMerged-> MergeRequestLabelRemoved; IssueLabelAdded-> IssueLabelAdded; IssueLabelAdded-> IssueLabelRemoved; IssueLabelAdded-> IssueClosed; IssueLabelRemoved-> IssueClosed; IssueFirstAddedToBoard-> IssueClosed; IssueFirstAddedToBoard-> IssueFirstAssociatedWithMilestone; IssueFirstAddedToBoard-> IssueFirstMentionedInCommit; IssueFirstAddedToBoard-> IssueLastEdited; IssueFirstAddedToBoard-> IssueLabelAdded; IssueFirstAddedToBoard-> IssueLabelRemoved; IssueFirstAssociatedWithMilestone-> IssueClosed; IssueFirstAssociatedWithMilestone-> IssueFirstAddedToBoard; IssueFirstAssociatedWithMilestone-> IssueFirstMentionedInCommit; IssueFirstAssociatedWithMilestone-> IssueLastEdited; IssueFirstAssociatedWithMilestone-> IssueLabelAdded; IssueFirstAssociatedWithMilestone-> IssueLabelRemoved; IssueFirstMentionedInCommit-> IssueClosed; IssueFirstMentionedInCommit-> IssueFirstAssociatedWithMilestone; IssueFirstMentionedInCommit-> IssueFirstAddedToBoard; IssueFirstMentionedInCommit-> IssueLastEdited; IssueFirstMentionedInCommit-> IssueLabelAdded; IssueFirstMentionedInCommit->已删除 IssueLabel; IssueClosed-> IssueLastEdited; IssueClosed-> IssueLabelAdded; IssueClosed-> IssueLabelRemoved; MergeRequestClosed-> MergeRequestFirstDeployedToProduction; MergeRequestClosed-> MergeRequestLastEdited; MergeRequestClosed-> MergeRequestLabelAdded; MergeRequestClosed-> MergeRequestLabelRemoved; MergeRequestFirstDeployedToProduction-> MergeRequestLastEdited; MergeRequestFirstDeployedToProduction-> MergeRequestLabelAdded; MergeRequestFirstDeployedToProduction-> MergeRequestLabelRemoved; MergeRequestLastBuildFinished-> MergeRequestClosed; MergeRequestLastBuildFinished-> MergeRequestFirstDeployedToProduction; MergeRequestLastBuildFinished-> MergeRequestLastEdited; MergeRequestLastBuildFinished-> MergeRequestMerged; MergeRequestLastBuildFinished-> MergeRequestLabelAdded; MergeRequestLastBuildFinished-> MergeRequestLabelRemoved; MergeRequestLabelAdded-> MergeRequestLabelAdded; MergeRequestLabelAdded-> MergeRequestLabelRemoved; MergeRequestLabelRemoved-> MergeRequestLabelAdded; MergeRequestLabelRemoved-> MergeRequestLabelRemoved;

### Parent[](#parent "Permalink")

团队和组织可能会定义自己的软件构建方式，因此阶段可能完全不同. 对于每个阶段，都需要定义一个父对象.

目前支持的父母：

*   `Project`
*   `Group`

#### How parent relationship it work[](#how-parent-relationship-it-work "Permalink")

1.  用户导航到价值流分析页面.
2.  用户选择一个组.
3.  后端将加载选定组的已定义阶段.
4.  对阶段的添加和修改将仅保留在所选组中.

### Default stages[](#default-stages "Permalink")

价值流分析的[原始实施](https://gitlab.com/gitlab-org/gitlab/-/issues/847)定义了 7 个阶段. 每个父母都可以使用这些阶段，但是无法更改这些阶段. 为了提高效率并减少创建的记录数，默认阶段被表示为内存中对象（不持久）. 当用户首次创建自定义阶段时，所有阶段都将保留. 此行为在价值流分析服务对象中实现. 这样做的原因是我们希望稍后添加隐藏和订购阶段的功能.

## Data Collector[](#data-collector "Permalink")

`DataCollector`是从数据库查询数据的中心点. 该类始终在单个阶段上运行，并且由以下组件组成：

*   `BaseQueryBuilder`:
    *   负责编写初始查询.
    *   处理特定于`Stage`配置：事件及其查询自定义.
    *   来自用户界面的参数：日期范围.
*   `Median` ：使用`BaseQueryBuilder`的查询计算一个阶段的中位数持续时间.
*   `RecordsFetcher` ：使用来自`BaseQueryBuilder`的查询和特定的`Finder`类加载阶段的相关记录，以应用可见性规则.
*   `DataForDurationChart` ：加载散点图的带有完成时间（结束事件时间戳）的计算的持续时间.

对于新的计算或查询，可将其实现为`DataCollector`类中的新方法调用.

## Database query[](#database-query "Permalink")

数据库查询的结构：

```
SELECT (customized by: Median or RecordsFetcher or DataForDurationChart)
FROM OBJECT_TYPE (Issue or MergeRequest)
INNER JOIN (several JOIN statements, depending on the events)
WHERE
  (Filter by the PARENT model, example: filter Issues from Project A)
  (Date range filter based on the OBJECT_TYPE.created_at)
  (Check if the START_EVENT is earlier than END_EVENT, preventing negative duration) 
```

`Median`的`SELECT`语句的结构：

```
SELECT (calculate median from START_EVENT_TIME-END_EVENT_TIME) 
```

用于`DataForDurationChart`的`SELECT`语句的`DataForDurationChart` ：

```
SELECT (START_EVENT_TIME-END_EVENT_TIME) as duration, END_EVENT.timestamp 
```

## High-level overview[](#high-level-overview "Permalink")

*   Rails 控制器（ `Analytics::CycleAnalytics`模块）：值流分析通过 JSON 端点公开其数据，该端点在`analytics`工作区中实现. 配置阶段还实现 JSON 端点（CRUD）.
*   服务（ `Analytics::CycleAnalytics`模块）：所有与`Stage`相关的操作都将委派给相应的服务对象.
*   模型（ `Analytics::CycleAnalytics`模块）：模型用于持久化`Stage`对象`ProjectStage`和`GroupStage` .
*   要素类（ `Gitlab::Analytics::CycleAnalytics`模块）：
    *   负责撰写查询并定义特定于功能的业务逻辑.
    *   `DataCollector` ， `Event` ， `StageEvents`等.

## Testing[](#testing "Permalink")

由于我们有很多事件和可能的配对，因此无法测试每个配对. 规则是至少要有一个使用`Event`类的测试用例.

使用新`Event`为阶段编写测试用例可能会遇到挑战，因为必须为两个事件都创建数据. 为了使此过程更简单，必须在`data_collector_spec.rb`中实现每个测试用例，在该`data_collector_spec.rb`中，通过`DataCollector`对该阶段进行测试. 每个测试用例都将变成多个测试，涵盖以下情况：

*   不同的父母： `Group`或`Project`
*   不同的计算方式： `Median` ， `RecordsFetcher`或`DataForDurationChart`