This is an automated email from the ASF dual-hosted git repository.

warren pushed a commit to branch feat-plugin-zentao
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/feat-plugin-zentao by this 
push:
     new 7671c7649 Fix update progress type (#3767)
7671c7649 is described below

commit 7671c7649f25186970125e0cc7a306b0b1c9cf72
Author: Warren Chen <[email protected]>
AuthorDate: Mon Nov 21 11:03:10 2022 +0800

    Fix update progress type (#3767)
    
    * fix(jenkins): update build stages logic (#3760)
    
    * feat(dora): update logic for change lead time (#3742)
    
    * feat(dora): update logic for change lead time
    
    closes #3516
    
    * feat(dora): update e2e according to review
    
    * fix(zentao): update progress typoe
---
 models/domainlayer/code/pull_request.go            |  35 ++--
 .../domainlayer/crossdomain/project_pr_metric.go   |  39 +++++
 .../20221111_add_project_pr_metric.go              |  63 +++++++
 .../archived/project_pr_metrics.go                 |  35 ++++
 models/migrationscripts/register.go                |   1 +
 .../dora/e2e/calculate_change_lead_time_test.go    |  61 ++-----
 .../dora/e2e/raw_tables/cicd_pipeline_commits.csv  |  17 ++
 plugins/dora/e2e/raw_tables/cicd_scopes.csv        |   4 +
 .../e2e/raw_tables/cicd_tasks_changeleadtime.csv   |  19 +++
 plugins/dora/e2e/raw_tables/commits_diffs.csv      |  13 ++
 plugins/dora/e2e/raw_tables/project_mapping.csv    |   6 +
 plugins/dora/e2e/raw_tables/pull_requests.csv      |  22 +--
 plugins/dora/e2e/raw_tables/repos.csv              |   7 +-
 .../e2e/snapshot_tables/project_pr_metrics.csv     |   7 +
 plugins/dora/e2e/snapshot_tables/pull_requests.csv |  10 --
 plugins/dora/tasks/change_lead_time_calculator.go  | 183 ++++++++++++---------
 plugins/dora/tasks/task_data.go                    |   2 +
 plugins/jenkins/e2e/builds_test.go                 |  23 ++-
 .../e2e/raw_tables/_raw_jenkins_api_jobs.csv       |   4 +-
 .../e2e/raw_tables/_raw_jenkins_api_stages.csv     |   3 +
 .../raw_tables/_tool_jenkins_builds_for_stages.csv |   2 +
 .../e2e/raw_tables/_tool_jenkins_stages.csv        |  20 +++
 .../e2e/snapshot_tables/_tool_jenkins_builds.csv   |  60 +++----
 .../_tool_jenkins_builds_after_enrich.csv          |  30 ++++
 plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv |   4 -
 plugins/jenkins/tasks/build_stages_enricher.go     |  43 ++---
 .../e2e/raw_tables/_raw_zentao_api_executions.csv  |   2 +-
 .../e2e/raw_tables/_raw_zentao_api_products.csv    |   2 +-
 .../e2e/raw_tables/_raw_zentao_api_tasks.csv       |   6 +-
 .../e2e/snapshot_tables/_tool_zentao_bugs.csv      |   2 +-
 .../snapshot_tables/_tool_zentao_executions.csv    |   2 +-
 .../e2e/snapshot_tables/_tool_zentao_products.csv  |   2 +-
 .../e2e/snapshot_tables/_tool_zentao_tasks.csv     |   8 +-
 plugins/zentao/models/archived/bug.go              |   2 +-
 plugins/zentao/models/archived/execution.go        |   4 +-
 plugins/zentao/models/archived/product.go          |   2 +-
 plugins/zentao/models/archived/project.go          |  20 +--
 plugins/zentao/models/archived/task.go             |   4 +-
 plugins/zentao/models/bug.go                       |   4 +-
 plugins/zentao/models/execution.go                 |  10 +-
 plugins/zentao/models/product.go                   |  26 +--
 plugins/zentao/models/project.go                   |  10 +-
 plugins/zentao/models/task.go                      |  16 +-
 plugins/zentao/tasks/bug_extractor.go              |   2 +-
 plugins/zentao/tasks/task_extractor.go             |   2 +-
 45 files changed, 538 insertions(+), 301 deletions(-)

diff --git a/models/domainlayer/code/pull_request.go 
b/models/domainlayer/code/pull_request.go
index 126884a80..e17e6970d 100644
--- a/models/domainlayer/code/pull_request.go
+++ b/models/domainlayer/code/pull_request.go
@@ -33,28 +33,19 @@ type PullRequest struct {
        Url         string `gorm:"type:varchar(255)"`
        AuthorName  string `gorm:"type:varchar(100)"`
        //User             domainUser.User `gorm:"foreignKey:AuthorId"`
-       AuthorId           string `gorm:"type:varchar(100)"`
-       ParentPrId         string `gorm:"index;type:varchar(100)"`
-       PullRequestKey     int
-       CreatedDate        time.Time
-       MergedDate         *time.Time
-       ClosedDate         *time.Time
-       Type               string `gorm:"type:varchar(100)"`
-       Component          string `gorm:"type:varchar(100)"`
-       MergeCommitSha     string `gorm:"type:varchar(40)"`
-       HeadRef            string `gorm:"type:varchar(255)"`
-       BaseRef            string `gorm:"type:varchar(255)"`
-       BaseCommitSha      string `gorm:"type:varchar(40)"`
-       HeadCommitSha      string `gorm:"type:varchar(40)"`
-       CodingTimespan     *int64
-       ReviewLag          *int64
-       ReviewTimespan     *int64
-       DeployTimespan     *int64
-       ChangeTimespan     *int64
-       OrigCodingTimespan int64
-       OrigReviewLag      int64
-       OrigReviewTimespan int64
-       OrigDeployTimespan int64
+       AuthorId       string `gorm:"type:varchar(100)"`
+       ParentPrId     string `gorm:"index;type:varchar(100)"`
+       PullRequestKey int
+       CreatedDate    time.Time
+       MergedDate     *time.Time
+       ClosedDate     *time.Time
+       Type           string `gorm:"type:varchar(100)"`
+       Component      string `gorm:"type:varchar(100)"`
+       MergeCommitSha string `gorm:"type:varchar(40)"`
+       HeadRef        string `gorm:"type:varchar(255)"`
+       BaseRef        string `gorm:"type:varchar(255)"`
+       BaseCommitSha  string `gorm:"type:varchar(40)"`
+       HeadCommitSha  string `gorm:"type:varchar(40)"`
 }
 
 func (PullRequest) TableName() string {
diff --git a/models/domainlayer/crossdomain/project_pr_metric.go 
b/models/domainlayer/crossdomain/project_pr_metric.go
new file mode 100644
index 000000000..f6c43f22b
--- /dev/null
+++ b/models/domainlayer/crossdomain/project_pr_metric.go
@@ -0,0 +1,39 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package crossdomain
+
+import (
+       "github.com/apache/incubator-devlake/models/domainlayer"
+)
+
+type ProjectPrMetric struct {
+       domainlayer.DomainEntity
+       ProjectName    string `gorm:"primaryKey;type:varchar(100)"`
+       FirstCommitSha string
+       CodingTimespan *int64
+       FirstReviewId  string
+       ReviewLag      *int64
+       ReviewTimespan *int64
+       DeploymentId   string
+       DeployTimespan *int64
+       ChangeTimespan *int64
+}
+
+func (ProjectPrMetric) TableName() string {
+       return "project_pr_metrics"
+}
diff --git a/models/migrationscripts/20221111_add_project_pr_metric.go 
b/models/migrationscripts/20221111_add_project_pr_metric.go
new file mode 100644
index 000000000..68f5782e0
--- /dev/null
+++ b/models/migrationscripts/20221111_add_project_pr_metric.go
@@ -0,0 +1,63 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+       "github.com/apache/incubator-devlake/errors"
+       "github.com/apache/incubator-devlake/helpers/migrationhelper"
+       "github.com/apache/incubator-devlake/models/migrationscripts/archived"
+       "github.com/apache/incubator-devlake/plugins/core"
+)
+
+type addProjectPrMetric struct{}
+
+func (u *addProjectPrMetric) Up(baseRes core.BasicRes) errors.Error {
+       db := baseRes.GetDal()
+       err := migrationhelper.AutoMigrateTables(
+               baseRes,
+               &archived.ProjectPrMetric{},
+       )
+       if err != nil {
+               return err
+       }
+       prColums := []string{
+               `coding_timespan`,
+               `review_lag`,
+               `review_timespan`,
+               `deploy_timespan`,
+               `change_timespan`,
+               `orig_coding_timespan`,
+               `orig_review_lag`,
+               `orig_review_timespan`,
+               `orig_deploy_timespan`,
+       }
+       err = db.DropColumns(`pull_requests`, prColums...)
+       if err != nil {
+               return err
+       }
+       err = db.DropColumns(`cicd_pipeline_commits`, "repo_url")
+       return err
+}
+
+func (*addProjectPrMetric) Version() uint64 {
+       return 20221111000001
+}
+
+func (*addProjectPrMetric) Name() string {
+       return "add project pr metric tables"
+}
diff --git a/models/migrationscripts/archived/project_pr_metrics.go 
b/models/migrationscripts/archived/project_pr_metrics.go
new file mode 100644
index 000000000..03e0f5252
--- /dev/null
+++ b/models/migrationscripts/archived/project_pr_metrics.go
@@ -0,0 +1,35 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package archived
+
+type ProjectPrMetric struct {
+       DomainEntity
+       ProjectName    string `gorm:"primaryKey;type:varchar(100)"`
+       FirstReviewId  string
+       FirstCommitSha string
+       CodingTimespan *int64
+       ReviewLag      *int64
+       ReviewTimespan *int64
+       DeploymentId   string
+       DeployTimespan *int64
+       ChangeTimespan *int64
+}
+
+func (ProjectPrMetric) TableName() string {
+       return "project_pr_metrics"
+}
diff --git a/models/migrationscripts/register.go 
b/models/migrationscripts/register.go
index 7e62d2a51..e28c33174 100644
--- a/models/migrationscripts/register.go
+++ b/models/migrationscripts/register.go
@@ -56,6 +56,7 @@ func All() []core.MigrationScript {
                new(addCicdScope),
                new(addSkipOnFail),
                new(modifyCommitsDiffs),
+               new(addProjectPrMetric),
                new(addProjectTables),
                new(addProjectToBluePrint),
        }
diff --git a/plugins/dora/e2e/calculate_change_lead_time_test.go 
b/plugins/dora/e2e/calculate_change_lead_time_test.go
index 599d8c483..9667a5040 100644
--- a/plugins/dora/e2e/calculate_change_lead_time_test.go
+++ b/plugins/dora/e2e/calculate_change_lead_time_test.go
@@ -18,13 +18,14 @@ limitations under the License.
 package e2e
 
 import (
-       "testing"
-
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
+       "github.com/apache/incubator-devlake/models/common"
        "github.com/apache/incubator-devlake/models/domainlayer/code"
+       "github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
        "github.com/apache/incubator-devlake/models/domainlayer/devops"
        "github.com/apache/incubator-devlake/plugins/dora/impl"
        "github.com/apache/incubator-devlake/plugins/dora/tasks"
+       "testing"
 )
 
 func TestCalculateCLTimeDataFlow(t *testing.T) {
@@ -33,7 +34,7 @@ func TestCalculateCLTimeDataFlow(t *testing.T) {
 
        taskData := &tasks.DoraTaskData{
                Options: &tasks.DoraOptions{
-                       RepoId: "github:GithubRepo:1:384111310",
+                       ProjectName: "project1",
                        TransformationRules: tasks.TransformationRules{
                                ProductionPattern: "(?i)deploy",
                        },
@@ -43,52 +44,22 @@ func TestCalculateCLTimeDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&code.PullRequest{})
 
        // import raw data table
-       dataflowTester.ImportCsvIntoTabler("./raw_tables/lake_cicd_tasks.csv", 
&devops.CICDTask{})
+       
dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks_changeleadtime.csv",
 &devops.CICDTask{})
        dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_requests.csv", 
&code.PullRequest{})
+       dataflowTester.ImportCsvIntoTabler("./raw_tables/commits_diffs.csv", 
&code.CommitsDiff{})
+       
dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_pipeline_commits.csv", 
&devops.CiCDPipelineCommit{})
+       dataflowTester.ImportCsvIntoTabler("./raw_tables/project_mapping.csv", 
&crossdomain.ProjectMapping{})
+       dataflowTester.ImportCsvIntoTabler("./raw_tables/commits.csv", 
&code.Commit{})
        
dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_request_comments.csv", 
&code.PullRequestComment{})
        
dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_request_commits.csv", 
&code.PullRequestCommit{})
-       dataflowTester.ImportCsvIntoTabler("./raw_tables/commits.csv", 
&code.Commit{})
+       dataflowTester.ImportCsvIntoTabler("./raw_tables/repos.csv", 
&code.Repo{})
+       dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_scopes.csv", 
&devops.CicdScope{})
 
        // verify converter
+       dataflowTester.FlushTabler(&crossdomain.ProjectPrMetric{})
        dataflowTester.Subtask(tasks.CalculateChangeLeadTimeMeta, taskData)
-       dataflowTester.VerifyTable(
-               code.PullRequest{},
-               "./snapshot_tables/pull_requests.csv",
-               []string{
-                       "id",
-                       "_raw_data_params",
-                       "_raw_data_table",
-                       "_raw_data_id",
-                       "_raw_data_remark",
-                       "base_repo_id",
-                       "head_repo_id",
-                       "status",
-                       "title",
-                       "description",
-                       "url",
-                       "author_name",
-                       "author_id",
-                       "parent_pr_id",
-                       "pull_request_key",
-                       "created_date",
-                       "merged_date",
-                       "closed_date",
-                       "type",
-                       "component",
-                       "merge_commit_sha",
-                       "head_ref",
-                       "base_ref",
-                       "base_commit_sha",
-                       "head_commit_sha",
-                       "coding_timespan",
-                       "review_lag",
-                       "review_timespan",
-                       "deploy_timespan",
-                       "change_timespan",
-                       "orig_coding_timespan",
-                       "orig_review_lag",
-                       "orig_review_timespan",
-                       "orig_deploy_timespan",
-               },
-       )
+       dataflowTester.VerifyTableWithOptions(&crossdomain.ProjectPrMetric{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/project_pr_metrics.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/plugins/dora/e2e/raw_tables/cicd_pipeline_commits.csv 
b/plugins/dora/e2e/raw_tables/cicd_pipeline_commits.csv
new file mode 100644
index 000000000..3c5029696
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/cicd_pipeline_commits.csv
@@ -0,0 +1,17 @@
+pipeline_id,commit_sha,branch,repo_id,repo_url
+pipeline110,commit300,z5z07j8qDO,repo1,
+pipeline111,commit301,7j3t5IFWb6,repo1,
+pipeline112,commit302,VBSqoF8WIh,repo1,
+pipeline113,commit303,VXyuBwhkIE,repo1,
+pipeline114,commit304,hJlTujPgFq,repo1,
+pipeline115,commit305,I8UmFLEYPO,repo2,
+pipeline116,commit306,oMhYUwGyVX,repo1,
+pipeline117,commit307,SQGBUB075n,repo1,
+pipeline118,commit308,XVxzMFClc5,repo1,
+pipeline119,commit309,Pr9qLgnNPD,repo1,
+pipeline18,commit22,QXxXzo0V6U,repo2,
+pipeline19,commit22,ey6y4W9jdm,repo3,
+pipeline19,commit24,wUYEULZ850,repo2,
+pipeline20,commit31,ptVxHa6jwh,repo1,
+pipeline21,commit32,RcX46MDXRQ,repo1,
+pipeline24,commit22,8LaxgnEhid,repo2,
diff --git a/plugins/dora/e2e/raw_tables/cicd_scopes.csv 
b/plugins/dora/e2e/raw_tables/cicd_scopes.csv
new file mode 100644
index 000000000..29cbc88d2
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/cicd_scopes.csv
@@ -0,0 +1,4 @@
+id
+cicd1
+cicd2
+cicd3
diff --git a/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv 
b/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv
new file mode 100644
index 000000000..b8acabbae
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv
@@ -0,0 +1,19 @@
+id,name,pipeline_id,status,result,type,environment,duration_sec,started_date,finished_date,cicd_scope_id
+task10,deployxIG,pipeline110,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-07-19 
22:06:28,2022-11-13 22:37:21,cicd1
+task11,deploya,pipeline111,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-08-06 
14:06:50,2022-11-13 00:07:21,cicd1
+task12,deployc,pipeline112,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-08-23 
17:44:05,2022-11-02 07:21:09,cicd2
+task13,deploy,pipeline113,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-08-30 
23:45:29,2022-11-28 00:46:47,cicd1
+task14,deployp0;,pipeline114,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-09-07 
02:49:26,2022-11-16 20:34:01,cicd1
+task15,deployY{,pipeline115,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-09-27 
01:07:50,2022-11-19 07:17:33,cicd2
+task16,deploy8',pipeline116,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-09-30 
21:05:38,2022-11-08 07:56:03,cicd1
+task17,deployKd%,pipeline117,IN_PROGRESS,,DEPLOYMENT,PRODUCTION,,2022-10-09 
06:42:02,,cicd1
+task19,deploy1,pipeline119,DONE,FAILURE,DEPLOYMENT,PRODUCTION,,2022-10-24 
18:41:04,2022-11-24 04:26:48,cicd1
+task21,deploy^^.,pipeline39,DONE,FAILURE,DEPLOYMENT,STAGING,,2004-01-10 
03:31:11,2022-11-28 20:41:59,cicd1
+task22,deploy,pipeline35,DONE,SUCCESS,DEPLOYMENT,TESTING,,2000-10-25 
09:57:28,2022-11-28 21:24:02,cicd1
+task23,deploy,pipeline36,IN_PROGRESS,,DEPLOYMENT,TESTING,,2005-02-07 
11:03:27,2022-11-05 18:18:03,cicd1
+task24,deploym,pipeline12,IN_PROGRESS,,DEPLOYMENT,STAGING,,2015-08-11 
19:58:06,2022-11-01 22:31:56,cicd1
+task25,deploy$p<,pipeline26,DONE,FAILURE,,TESTING,,2014-02-06 
13:42:43,2022-11-30 08:01:38,cicd3
+task26,deployb>@,pipeline20,IN_PROGRESS,,DEPLOYMENT,STAGING,,2016-08-26 
05:41:49,2022-11-15 07:31:46,cicd2
+task27,deployKfn,pipeline37,DONE,SUCCESS,DEPLOYMENT,STAGING,,2003-12-13 
23:19:14,2022-11-11 18:29:31,cicd2
+task28,deployl?,pipeline29,IN_PROGRESS,,DEPLOYMENT,TESTING,,2007-01-19 
01:13:39,2022-11-24 05:39:46,cicd3
+task29,deployUb,pipeline27,IN_PROGRESS,,DEPLOYMENT,TESTING,,2006-05-20 
18:17:13,2022-11-28 10:13:51,cicd2
diff --git a/plugins/dora/e2e/raw_tables/commits_diffs.csv 
b/plugins/dora/e2e/raw_tables/commits_diffs.csv
new file mode 100644
index 000000000..f81ed28c3
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/commits_diffs.csv
@@ -0,0 +1,13 @@
+commit_sha,new_commit_sha,old_commit_sha,sorting_index
+commit200,commit301,commit300,438
+commit201,commit302,commit301,491
+commit202,commit303,commit302,808
+commit203,commit304,commit303,247
+commit204,commit305,commit304,247
+commit3,commit24,commit25,58
+commit4,commit24,commit22,802
+commit5,commit23,commit23,355
+commit6,commit25,commit21,908
+commit7,commit27,commit23,47
+commit8,commit24,commit23,51
+commit9,commit22,commit20,957
diff --git a/plugins/dora/e2e/raw_tables/project_mapping.csv 
b/plugins/dora/e2e/raw_tables/project_mapping.csv
new file mode 100644
index 000000000..bcc085fc9
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/project_mapping.csv
@@ -0,0 +1,6 @@
+project_name,table,row_id
+project1,cicd_scopes,cicd1
+project1,cicd_scopes,cicd2
+project1,repos,repo1
+project1,repos,repo2
+project2,cicd_scopes,cicd3
diff --git a/plugins/dora/e2e/raw_tables/pull_requests.csv 
b/plugins/dora/e2e/raw_tables/pull_requests.csv
index b9c1a6684..e32c680a1 100644
--- a/plugins/dora/e2e/raw_tables/pull_requests.csv
+++ b/plugins/dora/e2e/raw_tables/pull_requests.csv
@@ -1,10 +1,12 @@
-id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,base_repo_id,head_repo_id,status,title,description,url,author_name,author_id,parent_pr_id,pull_request_key,created_date,merged_date,closed_date,type,component,merge_commit_sha,head_ref,base_ref,base_commit_sha,head_commit_sha,coding_timespan,review_lag,review_timespan,deploy_timespan,change_timespan,orig_coding_timespan,orig_review_lag,orig_review_timespan,orig_deploy_timespan
-github:GithubPullRequest:1:1043463302,2022-09-15 05:36:39.856,2022-09-15 
05:36:39.856,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13176,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat:
 implement API for plugin `customize`,"""# Summary\r\n\r\nfulfill the 
requirement \r\n#2880 ([Feature][customize] implement API for plugin customize) 
\r\n#2985 ([Feature][customize] new sub-task ExtractCustomizedFields for [...]
-github:GithubPullRequest:1:1048233599,2022-09-15 05:36:39.856,2022-09-15 
05:36:39.856,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13211,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat:
 new plugin for gitea,"""# Summary\r\n[Gitea](https://gitea.io/) is an 
open-source software package for hosting software development version control 
using Git as well as other collaborative features like bug tracking, code [...]
-github:GithubPullRequest:1:1049191985,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12680,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,[issue-2908]:
 Bump lake-builder version to 0.0.8,"""### ⚠️ Pre Checklist\r\n\r\n> Please 
complete _ALL_ items in this checklist, and remove before submitting\r\n\r\n- 
[x] I have read through the [Contributing Documentation](htt [...]
-github:GithubPullRequest:1:1051243958,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12691,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix:
 golangci-lint error,"""# Summary\r\n\r\ngolint-ci was down for a while, this 
PR try to fix all missed linting errors during the period.\r\n\r\n### 
Screenshots\r\n![image](https://user-images.githubusercontent.com/61080/189 
[...]
-github:GithubPullRequest:1:1051273681,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12693,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix:
 update pipelineId,"""# Summary\r\n\r\nupdate pipelineId\r\n\r\n### Does this 
close any open issues?\r\nrelated to #2998 \r\n\r\n### 
Screenshots\r\n![image](https://user-images.githubusercontent.com/101256042/189307562-88fc
 [...]
-github:GithubPullRequest:1:1051340471,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12694,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,Jenkins
 fix,"""# Summary\r\nAdd TestJenkinsStagesDataFlow.\r\nAdd path to stages 
collect.\r\n\r\n<!--\r\nThanks for submitting a pull request!\r\n\r\nWe 
appreciate you spending the time to work on these changes.\r\nPlease fill  [...]
-github:GithubPullRequest:1:1051524882,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12696,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix:
 task failure caused by 404,"""# Summary\r\nfix #2960 [Bug][gitihub] collect 
account failed by not found user\r\nIn the case of `err == 
ErrIgnoreAndContinue`, the `err` should not be wrapped, because on the caller 
side, the [...]
-github:GithubPullRequest:1:1051574863,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12697,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(jenkins):
 update e2e,"""# Summary\r\n\r\nUpdate e2e according to recent 
changes\r\n\r\n### Does this close any open issues?\r\nrelate to 
#2854\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots 
here.\r\n\r\n###  [...]
-github:GithubPullRequest:1:1051637383,2022-09-15 05:36:40.170,2022-09-15 
05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12699,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(gitlab):
 update e2e,"""# Summary\r\n\r\nupdate gitlab e2e according to recent 
changes\r\n\r\n### Does this close any open issues?\r\nrelate to 
#2871\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots 
here.\r\n\r [...]
+id,_raw_data_params,base_repo_id,created_date,merged_date,merge_commit_sha,base_commit_sha,head_commit_sha
+github:GithubPullRequest:1:1043463302,,repo1,2022-09-01 09:34:40,2022-09-09 
07:52:53,commit200,,
+github:GithubPullRequest:1:1048233599,,repo1,2022-09-07 04:07:39,2022-09-10 
02:35:43,commit201,,
+github:GithubPullRequest:1:1049191985,,repo1,2022-09-07 20:30:44,2022-09-09 
03:39:50,commit202,,
+github:GithubPullRequest:1:1051112182,,repo1,2022-09-09 12:35:29,2022-09-09 
13:32:51,commit203,,
+github:GithubPullRequest:1:1051524992,,repo1,2022-09-09 12:35:29,,commit111,,
+github:GithubPullRequest:1:1051524993,,repo1,2022-09-09 12:35:29,,commit112,,
+github:GithubPullRequest:1:1051524994,,repo1,2022-09-09 12:35:29,,commit113,,
+github:GithubPullRequest:1:1051574863,,repo1,2022-09-09 13:23:37,2022-09-09 
15:12:23,commit204,,
+github:GithubPullRequest:1:1051637383,,repo1,2022-09-09 14:17:24,2022-09-09 
15:16:28,commit92,,
+github:GithubPullRequest:2:1051123483,,repo4,2022-09-09 14:17:24,2022-09-09 
15:16:28,commit109,,
+github:GithubPullRequest:3:1051342463,,repo3,2022-09-09 13:23:37,2022-09-09 
15:12:23,commit102,,
diff --git a/plugins/dora/e2e/raw_tables/repos.csv 
b/plugins/dora/e2e/raw_tables/repos.csv
index 199b4f355..6cf992dec 100644
--- a/plugins/dora/e2e/raw_tables/repos.csv
+++ b/plugins/dora/e2e/raw_tables/repos.csv
@@ -1,2 +1,5 @@
-id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,url,description,owner_id,language,forked_from,created_date,updated_date,deleted
-github:GithubRepo:1:384111310,2022-09-20 13:47:48.519,2022-09-20 
13:47:48.519,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_repositories,1,"",apache/incubator-devlake,https://github.com/apache/incubator-devlake,"Apache
 DevLake is an open-source dev data platform to ingest, analyze, and visualize 
the fragmented data from DevOps tools, which can distill insights to improve 
engineering productivity.","",Go,"",2021-07-08 12:06:09,2022-09-20 03:25:21,0
+id
+github:GithubRepo:1:384111310
+repo1
+repo2
+repo3
diff --git a/plugins/dora/e2e/snapshot_tables/project_pr_metrics.csv 
b/plugins/dora/e2e/snapshot_tables/project_pr_metrics.csv
new file mode 100644
index 000000000..9391c7c40
--- /dev/null
+++ b/plugins/dora/e2e/snapshot_tables/project_pr_metrics.csv
@@ -0,0 +1,7 @@
+id,project_name,first_commit_sha,coding_timespan,first_review_id,review_lag,review_timespan,deployment_id,deploy_timespan,change_timespan
+github:GithubPullRequest:1:1043463302,project1,75ab753225b5b8acf3bc6e40e463b54b6800e7ed,,github:GithubPrReview:1:1098724785,8558,2859,task11,93134,104551
+github:GithubPullRequest:1:1048233599,project1,4f8cdefc9a9d53af16dd482c61623312eb9e9b5e,,github:GithubPrReview:1:0,194,4033,task12,76605,80832
+github:GithubPullRequest:1:1049191985,project1,4b71faf666833c0c7b915a512811e2c5e746d3de,1,github:GithubPrReview:1:1099918590,156,1712,task13,115026,116895
+github:GithubPullRequest:1:1051112182,project1,,,,,,task14,98341,98341
+github:GithubPullRequest:1:1051574863,project1,,,,,,,,
+github:GithubPullRequest:1:1051637383,project1,9d53fb594958e65456793caa1bfa8d07a7614291,1,,45,13,,,59
diff --git a/plugins/dora/e2e/snapshot_tables/pull_requests.csv 
b/plugins/dora/e2e/snapshot_tables/pull_requests.csv
deleted file mode 100644
index cab46ef5a..000000000
--- a/plugins/dora/e2e/snapshot_tables/pull_requests.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,base_repo_id,head_repo_id,status,title,description,url,author_name,author_id,parent_pr_id,pull_request_key,created_date,merged_date,closed_date,type,component,merge_commit_sha,head_ref,base_ref,base_commit_sha,head_commit_sha,coding_timespan,review_lag,review_timespan,deploy_timespan,change_timespan,orig_coding_timespan,orig_review_lag,orig_review_timespan,orig_deploy_timespan
-github:GithubPullRequest:1:1043463302,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13176,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat:
 implement API for plugin `customize`,"""# Summary\r\n\r\nfulfill the 
requirement \r\n#2880 ([Feature][customize] implement API for plugin customize) 
\r\n#2985 ([Feature][customize] new sub-task ExtractCustomizedFields for plugin 
customize)\r\n\r\nrelated to #2802 \r\nW [...]
-github:GithubPullRequest:1:1048233599,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13211,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat:
 new plugin for gitea,"""# Summary\r\n[Gitea](https://gitea.io/) is an 
open-source software package for hosting software development version control 
using Git as well as other collaborative features like bug tracking, code 
review, kanban boards, tickets, and wikis.\r\n< [...]
-github:GithubPullRequest:1:1049191985,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12680,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,[issue-2908]:
 Bump lake-builder version to 0.0.8,"""### ⚠️ Pre Checklist\r\n\r\n> Please 
complete _ALL_ items in this checklist, and remove before submitting\r\n\r\n- 
[x] I have read through the [Contributing 
Documentation](https://devlake.apache.org/community/).\r\n- [x] I  [...]
-github:GithubPullRequest:1:1051243958,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12691,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix:
 golangci-lint error,"""# Summary\r\n\r\ngolint-ci was down for a while, this 
PR try to fix all missed linting errors during the period.\r\n\r\n### 
Screenshots\r\n![image](https://user-images.githubusercontent.com/61080/189302830-d54ad54d-a3e6-470c-b517-9d803dbcd248.png)
 [...]
-github:GithubPullRequest:1:1051273681,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12693,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix:
 update pipelineId,"""# Summary\r\n\r\nupdate pipelineId\r\n\r\n### Does this 
close any open issues?\r\nrelated to #2998 \r\n\r\n### 
Screenshots\r\n![image](https://user-images.githubusercontent.com/101256042/189307562-88fc6b6d-5daf-4b9c-8cec-0493cfe5a42c.png)\r\n\r\n###
 [...]
-github:GithubPullRequest:1:1051340471,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12694,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,Jenkins
 fix,"""# Summary\r\nAdd TestJenkinsStagesDataFlow.\r\nAdd path to stages 
collect.\r\n\r\n<!--\r\nThanks for submitting a pull request!\r\n\r\nWe 
appreciate you spending the time to work on these changes.\r\nPlease fill out 
as many sections below as possible.\r\n-->\r [...]
-github:GithubPullRequest:1:1051524882,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12696,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix:
 task failure caused by 404,"""# Summary\r\nfix #2960 [Bug][gitihub] collect 
account failed by not found user\r\nIn the case of `err == 
ErrIgnoreAndContinue`, the `err` should not be wrapped, because on the caller 
side, the expression `err == ErrIgnoreAndContinue`  would [...]
-github:GithubPullRequest:1:1051574863,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12697,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(jenkins):
 update e2e,"""# Summary\r\n\r\nUpdate e2e according to recent 
changes\r\n\r\n### Does this close any open issues?\r\nrelate to 
#2854\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots 
here.\r\n\r\n### Other Information\r\nAny other information that  [...]
-github:GithubPullRequest:1:1051637383,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12699,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(gitlab):
 update e2e,"""# Summary\r\n\r\nupdate gitlab e2e according to recent 
changes\r\n\r\n### Does this close any open issues?\r\nrelate to 
#2871\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots 
here.\r\n\r\n### Other Information\r\nAny other information [...]
diff --git a/plugins/dora/tasks/change_lead_time_calculator.go 
b/plugins/dora/tasks/change_lead_time_calculator.go
index d6bcdb4b9..33e80f5da 100644
--- a/plugins/dora/tasks/change_lead_time_calculator.go
+++ b/plugins/dora/tasks/change_lead_time_calculator.go
@@ -19,12 +19,12 @@ package tasks
 
 import (
        goerror "errors"
+       "github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
        "reflect"
        "time"
 
        "github.com/apache/incubator-devlake/errors"
        "github.com/apache/incubator-devlake/models/domainlayer/code"
-       "github.com/apache/incubator-devlake/models/domainlayer/devops"
        "github.com/apache/incubator-devlake/plugins/core"
        "github.com/apache/incubator-devlake/plugins/core/dal"
        "github.com/apache/incubator-devlake/plugins/helper"
@@ -34,9 +34,41 @@ import (
 func CalculateChangeLeadTime(taskCtx core.SubTaskContext) errors.Error {
        db := taskCtx.GetDal()
        log := taskCtx.GetLogger()
+       data := taskCtx.GetData().(*DoraTaskData)
+       // construct a list of tuple[task, oldPipelineCommitSha, 
newPipelineCommitSha, taskFinishedDate]
+       pipelineIdClauses := []dal.Clause{
+               dal.Select(`ct.id as task_id, cpc.commit_sha as 
new_deploy_commit_sha, 
+                       ct.finished_date as task_finished_date, cpc.repo_id as 
repo_id`),
+               dal.From(`cicd_tasks ct`),
+               dal.Join(`left join cicd_pipeline_commits cpc on ct.pipeline_id 
= cpc.pipeline_id`),
+               dal.Join(`left join project_mapping pm on pm.row_id = 
ct.cicd_scope_id`),
+               dal.Where(`ct.environment = ? and ct.type = ? and ct.result = ? 
and pm.project_name = ? and pm.table = ?`,
+                       "PRODUCTION", "DEPLOYMENT", "SUCCESS", 
data.Options.ProjectName, "cicd_scopes"),
+               dal.Orderby(`cpc.repo_id, ct.started_date `),
+       }
+       deploymentPairList := make([]deploymentPair, 0)
+       err := db.All(&deploymentPairList, pipelineIdClauses...)
+       if err != nil {
+               return err
+       }
+       // deploymentPairList[i-1].NewDeployCommitSha is 
deploymentPairList[i].OldDeployCommitSha
+       oldDeployCommitSha := ""
+       lastRepoId := ""
+       for i := 0; i < len(deploymentPairList); i++ {
+               // if two deployments belong to different repo, let's skip
+               if lastRepoId == deploymentPairList[i].RepoId {
+                       deploymentPairList[i].OldDeployCommitSha = 
oldDeployCommitSha
+               } else {
+                       lastRepoId = deploymentPairList[i].RepoId
+               }
+               oldDeployCommitSha = deploymentPairList[i].NewDeployCommitSha
+       }
+
+       // get prs by repo project_name
        clauses := []dal.Clause{
                dal.From(&code.PullRequest{}),
-               dal.Where("merged_date IS NOT NULL"),
+               dal.Join(`left join project_mapping pm on pm.row_id = 
pull_requests.base_repo_id`),
+               dal.Where("pull_requests.merged_date IS NOT NULL and 
pm.project_name = ? and pm.table = ?", data.Options.ProjectName, "repos"),
        }
        cursor, err := db.Cursor(clauses...)
        if err != nil {
@@ -44,11 +76,11 @@ func CalculateChangeLeadTime(taskCtx core.SubTaskContext) 
errors.Error {
        }
        defer cursor.Close()
 
-       enricher, err := helper.NewDataConverter(helper.DataConverterArgs{
+       converter, err := helper.NewDataConverter(helper.DataConverterArgs{
                RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
-                       Ctx:    taskCtx,
+                       Ctx: taskCtx,
                        Params: DoraApiParams{
-                               // TODO
+                               ProjectName: data.Options.ProjectName,
                        },
                        Table: "pull_requests",
                },
@@ -57,66 +89,74 @@ func CalculateChangeLeadTime(taskCtx core.SubTaskContext) 
errors.Error {
                Input:        cursor,
                Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
                        pr := inputRow.(*code.PullRequest)
-                       firstCommitDate, err := getFirstCommitTime(pr.Id, db)
+                       firstCommit, err := getFirstCommit(pr.Id, db)
+                       if err != nil {
+                               return nil, err
+                       }
+                       projectPrMetric := &crossdomain.ProjectPrMetric{}
+                       projectPrMetric.Id = pr.Id
+                       projectPrMetric.ProjectName = data.Options.ProjectName
                        if err != nil {
                                return nil, err
                        }
-                       if firstCommitDate != nil {
-                               codingTime := 
int64(pr.CreatedDate.Sub(*firstCommitDate).Seconds())
+                       if firstCommit != nil {
+                               codingTime := 
int64(pr.CreatedDate.Sub(firstCommit.AuthoredDate).Seconds())
                                if codingTime/60 == 0 && codingTime%60 > 0 {
                                        codingTime = 1
                                } else {
                                        codingTime = codingTime / 60
                                }
-                               pr.OrigCodingTimespan = codingTime
+                               projectPrMetric.CodingTimespan = 
processNegativeValue(codingTime)
+                               projectPrMetric.FirstCommitSha = firstCommit.Sha
                        }
-                       firstReviewTime, err := getFirstReviewTime(pr.Id, 
pr.AuthorId, db)
+                       firstReview, err := getFirstReview(pr.Id, pr.AuthorId, 
db)
                        if err != nil {
                                return nil, err
                        }
-                       if firstReviewTime != nil {
-                               pr.OrigReviewLag = 
int64(firstReviewTime.Sub(pr.CreatedDate).Minutes())
-                               pr.OrigReviewTimespan = 
int64(pr.MergedDate.Sub(*firstReviewTime).Minutes())
+                       if firstReview != nil {
+                               projectPrMetric.ReviewLag = 
processNegativeValue(int64(firstReview.CreatedDate.Sub(pr.CreatedDate).Minutes()))
+                               projectPrMetric.ReviewTimespan = 
processNegativeValue(int64(pr.MergedDate.Sub(firstReview.CreatedDate).Minutes()))
+                               projectPrMetric.FirstReviewId = 
firstReview.ReviewId
                        }
-                       deployment, err := getDeployment(devops.PRODUCTION, 
*pr.MergedDate, db)
+                       deployment, err := getDeployment(pr.MergeCommitSha, 
pr.BaseRepoId, deploymentPairList, db)
                        if err != nil {
                                return nil, err
                        }
-                       if deployment != nil && deployment.FinishedDate != nil {
-                               timespan := 
deployment.FinishedDate.Sub(*pr.MergedDate)
-                               pr.OrigDeployTimespan = 
int64(timespan.Minutes())
+                       if deployment != nil && deployment.TaskFinishedDate != 
nil {
+                               timespan := 
deployment.TaskFinishedDate.Sub(*pr.MergedDate)
+                               projectPrMetric.DeployTimespan = 
processNegativeValue(int64(timespan.Minutes()))
+                               projectPrMetric.DeploymentId = deployment.TaskId
                        } else {
                                log.Debug("deploy time of pr %v is nil\n", 
pr.PullRequestKey)
                        }
-                       processNegativeValue(pr)
-                       pr.ChangeTimespan = nil
-                       result := int64(0)
-                       if pr.CodingTimespan != nil {
-                               result += *pr.CodingTimespan
+                       projectPrMetric.ChangeTimespan = nil
+                       var result int64
+                       if projectPrMetric.CodingTimespan != nil {
+                               result += *projectPrMetric.CodingTimespan
                        }
-                       if pr.ReviewLag != nil {
-                               result += *pr.ReviewLag
+                       if projectPrMetric.ReviewLag != nil {
+                               result += *projectPrMetric.ReviewLag
                        }
-                       if pr.ReviewTimespan != nil {
-                               result += *pr.ReviewTimespan
+                       if projectPrMetric.ReviewTimespan != nil {
+                               result += *projectPrMetric.ReviewTimespan
                        }
-                       if pr.DeployTimespan != nil {
-                               result += *pr.DeployTimespan
+                       if projectPrMetric.DeployTimespan != nil {
+                               result += *projectPrMetric.DeployTimespan
                        }
                        if result > 0 {
-                               pr.ChangeTimespan = &result
+                               projectPrMetric.ChangeTimespan = &result
                        }
-                       return []interface{}{pr}, nil
+                       return []interface{}{projectPrMetric}, nil
                },
        })
        if err != nil {
                return err
        }
 
-       return enricher.Execute()
+       return converter.Execute()
 }
 
-func getFirstCommitTime(prId string, db dal.Dal) (*time.Time, errors.Error) {
+func getFirstCommit(prId string, db dal.Dal) (*code.Commit, errors.Error) {
        commit := &code.Commit{}
        commitClauses := []dal.Clause{
                dal.From(&code.Commit{}),
@@ -131,10 +171,10 @@ func getFirstCommitTime(prId string, db dal.Dal) 
(*time.Time, errors.Error) {
        if err != nil {
                return nil, err
        }
-       return &commit.AuthoredDate, nil
+       return commit, nil
 }
 
-func getFirstReviewTime(prId string, prCreator string, db dal.Dal) 
(*time.Time, errors.Error) {
+func getFirstReview(prId string, prCreator string, db dal.Dal) 
(*code.PullRequestComment, errors.Error) {
        review := &code.PullRequestComment{}
        commentClauses := []dal.Clause{
                dal.From(&code.PullRequestComment{}),
@@ -148,57 +188,40 @@ func getFirstReviewTime(prId string, prCreator string, db 
dal.Dal) (*time.Time,
        if err != nil {
                return nil, err
        }
-       return &review.CreatedDate, nil
+       return review, nil
 }
 
-func getDeployment(environment string, mergeDate time.Time, db dal.Dal) 
(*devops.CICDTask, errors.Error) {
+func getDeployment(mergeSha string, repoId string, deploymentPairList 
[]deploymentPair, db dal.Dal) (*deploymentPair, errors.Error) {
        // ignore environment at this point because detecting it by name is 
obviously not engouh
        // take 
https://github.com/apache/incubator-devlake/actions/workflows/build.yml for 
example
        // one can not distingush testing/production by looking at the job name 
solely.
-       cicdTask := &devops.CICDTask{}
-       cicdTaskClauses := []dal.Clause{
-               dal.From(&devops.CICDTask{}),
-               dal.Where(`
-                       type = ?
-                       AND cicd_tasks.result = ?
-                       AND cicd_tasks.started_date > ?`,
-                       "DEPLOYMENT",
-                       "SUCCESS",
-                       mergeDate,
-               ),
-               dal.Orderby("cicd_tasks.started_date ASC"),
-               dal.Limit(1),
-       }
-       err := db.First(cicdTask, cicdTaskClauses...)
-       if goerror.Is(err, gorm.ErrRecordNotFound) {
-               return nil, nil
-       }
-       if err != nil {
-               return nil, err
+       commitDiff := &code.CommitsDiff{}
+       // find if tuple[merge_sha, new_commit_sha, old_commit_sha] exist in 
commits_diffs, if yes, return pair.FinishedDate
+       for _, pair := range deploymentPairList {
+               if repoId != pair.RepoId {
+                       continue
+               }
+               err := db.First(commitDiff, dal.Where(`commit_sha = ? and 
new_commit_sha = ? and old_commit_sha = ?`,
+                       mergeSha, pair.NewDeployCommitSha, 
pair.OldDeployCommitSha))
+               if err == nil {
+                       return &pair, nil
+               }
+               if goerror.Is(err, gorm.ErrRecordNotFound) {
+                       continue
+               }
+               if err != nil {
+                       return nil, err
+               }
+
        }
-       return cicdTask, nil
+       return nil, nil
 }
 
-func processNegativeValue(pr *code.PullRequest) {
-       if pr.OrigCodingTimespan > 0 {
-               pr.CodingTimespan = &pr.OrigCodingTimespan
+func processNegativeValue(v int64) *int64 {
+       if v > 0 {
+               return &v
        } else {
-               pr.CodingTimespan = nil
-       }
-       if pr.OrigReviewLag > 0 {
-               pr.ReviewLag = &pr.OrigReviewLag
-       } else {
-               pr.ReviewLag = nil
-       }
-       if pr.OrigReviewTimespan > 0 {
-               pr.ReviewTimespan = &pr.OrigReviewTimespan
-       } else {
-               pr.ReviewTimespan = nil
-       }
-       if pr.OrigDeployTimespan > 0 {
-               pr.DeployTimespan = &pr.OrigDeployTimespan
-       } else {
-               pr.DeployTimespan = nil
+               return nil
        }
 }
 
@@ -209,3 +232,11 @@ var CalculateChangeLeadTimeMeta = core.SubTaskMeta{
        Description:      "Calculate change lead time",
        DomainTypes:      []string{core.DOMAIN_TYPE_CICD, 
core.DOMAIN_TYPE_CODE},
 }
+
+type deploymentPair struct {
+       TaskId             string
+       RepoId             string
+       NewDeployCommitSha string
+       OldDeployCommitSha string
+       TaskFinishedDate   *time.Time
+}
diff --git a/plugins/dora/tasks/task_data.go b/plugins/dora/tasks/task_data.go
index 9345eaabf..16ce528af 100644
--- a/plugins/dora/tasks/task_data.go
+++ b/plugins/dora/tasks/task_data.go
@@ -23,6 +23,7 @@ import (
 )
 
 type DoraApiParams struct {
+       ProjectName string
 }
 
 type TransformationRules struct {
@@ -36,6 +37,7 @@ type DoraOptions struct {
        Since               string
        RepoId              string `json:"repoId"`
        Prefix              string `json:"prefix"`
+       ProjectName         string `json:"projectName"`
        TransformationRules `mapstructure:"transformationRules" 
json:"transformationRules"`
 }
 
diff --git a/plugins/jenkins/e2e/builds_test.go 
b/plugins/jenkins/e2e/builds_test.go
index 5a925c096..4fee31bef 100644
--- a/plugins/jenkins/e2e/builds_test.go
+++ b/plugins/jenkins/e2e/builds_test.go
@@ -41,15 +41,15 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
                Job: &models.JenkinsJob{FullName: "Test-jenkins-dir » 
test-jenkins-sub-dir » test-sub-sub-dir » devlake"},
        }
 
-       // import raw data table
-       // SELECT * FROM _raw_jenkins_api_builds INTO OUTFILE 
"/tmp/_raw_jenkins_api_builds.csv" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED 
BY '"' LINES TERMINATED BY '\r\n';
-       
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jenkins_api_builds.csv",
 "_raw_jenkins_api_builds")
-
-       // verify extraction
        dataflowTester.FlushTabler(&models.JenkinsBuild{})
        dataflowTester.FlushTabler(&models.JenkinsBuildCommit{})
        dataflowTester.FlushTabler(&models.JenkinsStage{})
 
+       // import raw data table
+       // SELECT * FROM _raw_jenkins_api_builds INTO OUTFILE 
"/tmp/_raw_jenkins_api_builds.csv" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED 
BY '"' LINES TERMINATED BY '\r\n';
+       
dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jenkins_api_builds.csv",
 "_raw_jenkins_api_builds")
+       
dataflowTester.ImportCsvIntoTabler("./raw_tables/_tool_jenkins_stages.csv", 
&models.JenkinsStage{})
+
        dataflowTester.Subtask(tasks.ExtractApiBuildsMeta, taskData)
        dataflowTester.VerifyTable(
                models.JenkinsBuild{},
@@ -65,6 +65,7 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
                        "result",
                        "timestamp",
                        "start_time",
+                       "has_stages",
                ),
        )
 
@@ -84,6 +85,18 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&devops.CICDPipeline{})
        dataflowTester.FlushTabler(&devops.CiCDPipelineCommit{})
        dataflowTester.Subtask(tasks.EnrichApiBuildWithStagesMeta, taskData)
+       dataflowTester.VerifyTable(
+               models.JenkinsBuild{},
+               "./snapshot_tables/_tool_jenkins_builds_after_enrich.csv",
+               []string{
+                       "connection_id",
+                       "job_name",
+                       "duration",
+                       "full_display_name",
+                       "has_stages",
+               },
+       )
+
        dataflowTester.Subtask(tasks.ConvertBuildsToCICDMeta, taskData)
        dataflowTester.Subtask(tasks.ConvertBuildReposMeta, taskData)
 
diff --git a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv 
b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv
index 68a632b1b..b995fc323 100644
--- a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv
+++ b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv
@@ -1,2 +1,4 @@
 "id","params","data","url","input","created_at"
-16247,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test-
 [...]
\ No newline at end of file
+16247,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test-
 [...]
+16248,"{""ConnectionId"":2,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test-
 [...]
+16249,"{""ConnectionId"":1,""JobName"":""devlake1"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test
 [...]
diff --git a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv 
b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv
index ecae6ca92..eb3037a91 100644
--- a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv
+++ b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv
@@ -15,3 +15,6 @@
 
13581,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/12/execution/node/6/wfapi/describe""}},""id"":""3"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981125465,""durationMillis"":83641,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/12/wfapi/describe,"{""Number"":
 ""1 [...]
 
13582,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Auto%20Init%20Gitlab/23/execution/node/6/wfapi/describe""}},""id"":""4"",""name"":""gitlabInit"",""execNode"":"""",""status"":null,""error"":{""message"":null,""type"":""org.jenkinsci.plugins.workflow.steps.FlowInterruptedException""},""startTimeMillis"":1605767269680,""durationMillis"":248279,""pauseDurationMilli
 [...]
 
13583,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/10/execution/node/6/wfapi/describe""}},""id"":""5"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981120136,""durationMillis"":86044,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/10/wfapi/describe,"{""Number"":
 ""1 [...]
+13584,"{""ConnectionId"":2,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/12/execution/node/6/wfapi/describe""}},""id"":""3"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981125465,""durationMillis"":83641,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/12/wfapi/describe,"{""Number"":
 ""1 [...]
+13585,"{""ConnectionId"":2,""JobName"":""devlake1"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Auto%20Init%20Gitlab/23/execution/node/6/wfapi/describe""}},""id"":""4"",""name"":""gitlabInit"",""execNode"":"""",""status"":null,""error"":{""message"":null,""type"":""org.jenkinsci.plugins.workflow.steps.FlowInterruptedException""},""startTimeMillis"":1605767269680,""durationMillis"":248279,""pauseDurationMill
 [...]
+13586,"{""ConnectionId"":3,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/10/execution/node/6/wfapi/describe""}},""id"":""5"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981120136,""durationMillis"":86044,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/10/wfapi/describe,"{""Number"":
 ""1 [...]
diff --git a/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv 
b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv
index 3fbc8469c..8ca916477 100644
--- a/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv
+++ b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv
@@ -13,3 +13,5 @@ 
connection_id,full_display_name,job_name,job_path,duration,estimated_duration,nu
 1,Test Gitlab Sync 
#12,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,9,3,SUCCESS,1658386255052,2022-07-21T06:50:55.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,110,
 1,Auto Init Gitlab 
#23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,9,4,SUCCESS,1662647217746,2022-09-08T14:26:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,109,
 1,Test Gitlab Sync 
#10,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
+2,Auto Init Gitlab 
#23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,9,4,SUCCESS,1662647217746,2022-09-08T14:26:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,109,
+2,Test Gitlab Sync 
#10,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
diff --git a/plugins/jenkins/e2e/raw_tables/_tool_jenkins_stages.csv 
b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_stages.csv
new file mode 100644
index 000000000..e7fbd0784
--- /dev/null
+++ b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_stages.csv
@@ -0,0 +1,20 @@
+connection_id,id,build_name,name,exec_node,status,start_time_millis,duration_millis,pause_duration_millis,type,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#11,gitlabAutoSync,,SUCCESS,1581076468986,14118,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13579,
+1,10,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#13,Hello,,SUCCESS,1662651617917,258,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,5,
+1,11,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#15,Hello,,SUCCESS,1662651634424,79,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,6,
+1,12,Auto Init Gitlab 
#1,gitlabInit,,FAILED,1583329644046,588,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13574,
+1,2,Auto Init Gitlab 
#19,gitlabInit,,ABORTED,1584497957140,5859,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13580,
+1,3,Test Gitlab Sync 
#12,gitlabAutoSync,,SUCCESS,1583981125465,83641,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13581,
+1,4,Auto Init Gitlab 
#23,gitlabInit,,,1605767269680,248279,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13582,
+1,5,Test Gitlab Sync 
#10,gitlabAutoSync,,SUCCESS,1583981120136,86044,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13583,
+1,6,Auto Init Gitlab 
#18,gitlabInit,,FAILED,1584458835920,215100,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13575,
+1,6,pipeline-test2 
#2,Hello,,SUCCESS,1662651634424,79,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,1,
+1,7,Auto Init Gitlab 
#19,gitlabInit,,ABORTED,1584497957140,5859,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13576,
+1,7,pipeline-test2 
#3,Hello,,SUCCESS,1662651649629,122,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,2,
+1,8,Pipeline expirement 
#5,scp-f/b,,FAILED,1572321694770,297,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13577,
+1,8,pipeline-test2 
#3,Hello,,SUCCESS,1662651649629,122,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,3,
+1,9,Auto Init Gitlab 
#58,gitlabInit,,,1615296910614,1312274,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13578,
+1,9,pipeline-test2 
#1,Hello,,SUCCESS,1662651617917,258,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,4,
+2,8,pipeline-test2 
#3,Hello,,SUCCESS,1662651649629,122,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,3,
+2,9,Auto Init Gitlab 
#58,gitlabInit,,,1615296910614,1312274,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13578,
+2,9,pipeline-test2 
#1,Hello,,SUCCESS,1662651617917,258,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,4,
diff --git a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv 
b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv
index e33e71886..62e819449 100644
--- a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv
@@ -1,30 +1,30 @@
-connection_id,full_display_name,job_name,job_path,duration,estimated_duration,number,result,timestamp,start_time,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-1,pipeline-test2 
#1,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4564,1972,1,SUCCESS,1662651613681,2022-09-08T15:40:13.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,100,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#11,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,14820,1457,1,SUCCESS,1650017416514,2022-04-15T10:10:16.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,95,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#13,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#15,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,70,27,1,SUCCESS,1658385566471,2022-07-21T06:39:26.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,105,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#17,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,57,6,1,SUCCESS,1650017153775,2022-04-15T10:05:53.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,124,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#170,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,12,6,10,SUCCESS,1662647233074,2022-09-08T14:27:13.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,115,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#171,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,11,SUCCESS,1662651656567,2022-09-08T15:40:56.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,114,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#172,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2,6,12,SUCCESS,1662651657893,2022-09-08T15:40:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,113,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#21,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2121,1457,2,SUCCESS,1650022548450,2022-04-15T11:35:48.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,94,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#215,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,8,2,SUCCESS,1662647212436,2022-09-08T14:26:52.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,101,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,61,745,2,SUCCESS,1662647211512,2022-09-08T14:26:51.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,96,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#24,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,551,1972,2,SUCCESS,1662651633991,2022-09-08T15:40:33.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,99,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#25,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,27,2,SUCCESS,1658385576367,2022-07-21T06:39:36.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,104,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#27,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,2,SUCCESS,1650017177939,2022-04-15T10:06:17.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,123,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#31,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1587,1457,3,SUCCESS,1650024049161,2022-04-15T12:00:49.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,93,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#34,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,802,1972,3,SUCCESS,1662651648992,2022-09-08T15:40:48.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,98,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#35,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,27,3,SUCCESS,1662647217041,2022-09-08T14:26:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,103,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#37,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,3,6,3,SUCCESS,1650017186253,2022-04-15T10:06:26.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,122,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#41,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,13952,1457,4,SUCCESS,1662647203905,2022-09-08T14:26:43.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,92,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#47,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,4,SUCCESS,1650022556910,2022-04-15T11:35:56.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,121,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#51,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1274,1457,5,SUCCESS,1662647231332,2022-09-08T14:27:11.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,91,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#57,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,5,SUCCESS,1650022558491,2022-04-15T11:35:58.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,120,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#61,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1202,1457,6,SUCCESS,1662647242809,2022-09-08T14:27:22.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,90,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#67,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,10,6,6,SUCCESS,1650022560954,2022-04-15T11:36:00.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,119,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#71,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1988,1457,7,SUCCESS,1662651625889,2022-09-08T15:40:25.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,89,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#77,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,8,6,7,SUCCESS,1650023883294,2022-04-15T11:58:03.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,118,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#81,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1180,1457,8,SUCCESS,1662651640536,2022-09-08T15:40:40.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,88,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#87,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,6,8,SUCCESS,1650023894336,2022-04-15T11:58:14.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,117,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#97,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,78,6,9,SUCCESS,1662647207972,2022-09-08T14:26:47.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,116,
+connection_id,full_display_name,job_name,job_path,duration,estimated_duration,number,result,timestamp,start_time,has_stages,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,pipeline-test2 
#1,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4564,1972,1,SUCCESS,1662651613681,2022-09-08T15:40:13.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,100,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#11,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,14820,1457,1,SUCCESS,1650017416514,2022-04-15T10:10:16.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,95,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#13,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#15,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,70,27,1,SUCCESS,1658385566471,2022-07-21T06:39:26.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,105,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#17,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,57,6,1,SUCCESS,1650017153775,2022-04-15T10:05:53.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,124,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#170,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,12,6,10,SUCCESS,1662647233074,2022-09-08T14:27:13.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,115,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#171,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,11,SUCCESS,1662651656567,2022-09-08T15:40:56.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,114,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#172,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2,6,12,SUCCESS,1662651657893,2022-09-08T15:40:57.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,113,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#21,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2121,1457,2,SUCCESS,1650022548450,2022-04-15T11:35:48.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,94,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#215,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,8,2,SUCCESS,1662647212436,2022-09-08T14:26:52.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,101,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,61,745,2,SUCCESS,1662647211512,2022-09-08T14:26:51.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,96,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#24,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,551,1972,2,SUCCESS,1662651633991,2022-09-08T15:40:33.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,99,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#25,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,27,2,SUCCESS,1658385576367,2022-07-21T06:39:36.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,104,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#27,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,2,SUCCESS,1650017177939,2022-04-15T10:06:17.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,123,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#31,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1587,1457,3,SUCCESS,1650024049161,2022-04-15T12:00:49.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,93,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#34,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,802,1972,3,SUCCESS,1662651648992,2022-09-08T15:40:48.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,98,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#35,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,27,3,SUCCESS,1662647217041,2022-09-08T14:26:57.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,103,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#37,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,3,6,3,SUCCESS,1650017186253,2022-04-15T10:06:26.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,122,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#41,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,13952,1457,4,SUCCESS,1662647203905,2022-09-08T14:26:43.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,92,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#47,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,4,SUCCESS,1650022556910,2022-04-15T11:35:56.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,121,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#51,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1274,1457,5,SUCCESS,1662647231332,2022-09-08T14:27:11.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,91,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#57,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,5,SUCCESS,1650022558491,2022-04-15T11:35:58.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,120,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#61,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1202,1457,6,SUCCESS,1662647242809,2022-09-08T14:27:22.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,90,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#67,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,10,6,6,SUCCESS,1650022560954,2022-04-15T11:36:00.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,119,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#71,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1988,1457,7,SUCCESS,1662651625889,2022-09-08T15:40:25.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,89,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#77,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,8,6,7,SUCCESS,1650023883294,2022-04-15T11:58:03.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,118,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#81,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1180,1457,8,SUCCESS,1662651640536,2022-09-08T15:40:40.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,88,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#87,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,6,8,SUCCESS,1650023894336,2022-04-15T11:58:14.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,117,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#97,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,78,6,9,SUCCESS,1662647207972,2022-09-08T14:26:47.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,116,
diff --git 
a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds_after_enrich.csv 
b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds_after_enrich.csv
new file mode 100644
index 000000000..fa929f45e
--- /dev/null
+++ b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds_after_enrich.csv
@@ -0,0 +1,30 @@
+connection_id,full_display_name,job_name,duration,has_stages
+1,pipeline-test2 #1,devlake,4564,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#11,devlake,14820,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#13,devlake,1429,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#15,devlake,70,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#17,devlake,57,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#170,devlake,12,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#171,devlake,4,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#172,devlake,2,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#21,devlake,2121,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#215,devlake,11,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#23,devlake,61,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#24,devlake,551,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#25,devlake,6,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#27,devlake,4,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#31,devlake,1587,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#34,devlake,802,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#35,devlake,4,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#37,devlake,3,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#41,devlake,13952,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#47,devlake,6,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#51,devlake,1274,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#57,devlake,6,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#61,devlake,1202,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#67,devlake,10,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#71,devlake,1988,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#77,devlake,8,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#81,devlake,1180,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#87,devlake,11,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#97,devlake,78,0
diff --git a/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv 
b/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv
index cb798e9a3..ef155677d 100644
--- a/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv
@@ -1,8 +1,4 @@
 
id,name,pipeline_id,result,status,type,environment,duration_sec,started_date,finished_date,cicd_scope_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-jenkins:JenkinsBuild:1:pipeline-test2 
#1,devlake,jenkins:JenkinsBuild:1:pipeline-test2 
#1,SUCCESS,DONE,,,4,2022-09-08T15:40:13.000+00:00,2022-09-08T15:40:17.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,100,
-jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » 
test-sub-sub-dir » devlake #11,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir 
» test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#11,SUCCESS,DONE,,,14,2022-04-15T10:10:16.000+00:00,2022-04-15T10:10:30.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-di
 [...]
-jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » 
test-sub-sub-dir » devlake #13,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir 
» test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#13,SUCCESS,DONE,,,1,2022-07-21T06:40:02.000+00:00,2022-07-21T06:40:03.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir
 [...]
-jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » 
test-sub-sub-dir » devlake #15,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir 
» test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#15,SUCCESS,DONE,,,0,2022-07-21T06:39:26.000+00:00,2022-07-21T06:39:26.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir
 [...]
 jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » 
test-sub-sub-dir » devlake #17,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir 
» test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#17,SUCCESS,DONE,,,0,2022-04-15T10:05:53.000+00:00,2022-04-15T10:05:53.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir
 [...]
 jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » 
test-sub-sub-dir » devlake #170,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir 
» test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#170,SUCCESS,DONE,,,0,2022-09-08T14:27:13.000+00:00,2022-09-08T14:27:13.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-d
 [...]
 jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » 
test-sub-sub-dir » devlake #171,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir 
» test-jenkins-sub-dir » test-sub-sub-dir » devlake 
#171,SUCCESS,DONE,,,0,2022-09-08T15:40:56.000+00:00,2022-09-08T15:40:56.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir
 » test-jenkins-sub-dir » test-sub-sub-dir » 
devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-d
 [...]
diff --git a/plugins/jenkins/tasks/build_stages_enricher.go 
b/plugins/jenkins/tasks/build_stages_enricher.go
index d223e66e3..8ce74eb21 100644
--- a/plugins/jenkins/tasks/build_stages_enricher.go
+++ b/plugins/jenkins/tasks/build_stages_enricher.go
@@ -22,12 +22,8 @@ import (
        "github.com/apache/incubator-devlake/plugins/core"
        "github.com/apache/incubator-devlake/plugins/core/dal"
        "github.com/apache/incubator-devlake/plugins/jenkins/models"
-       "strconv"
-       "strings"
 )
 
-// this struct should be moved to `gitub_api_common.go`
-
 var EnrichApiBuildWithStagesMeta = core.SubTaskMeta{
        Name:             "enrichApiBuildWithStages",
        EntryPoint:       EnrichApiBuildWithStages,
@@ -40,15 +36,14 @@ func EnrichApiBuildWithStages(taskCtx core.SubTaskContext) 
errors.Error {
        data := taskCtx.GetData().(*JenkinsTaskData)
        db := taskCtx.GetDal()
        clauses := []dal.Clause{
-               dal.Select("distinct build_name"),
-               dal.From(&models.JenkinsStage{}),
-               dal.Join(`left join _tool_jenkins_builds tjb 
-                                               on 
_tool_jenkins_stages.build_name = tjb.full_display_name 
-                                               and 
_tool_jenkins_stages.connection_id = tjb.connection_id`),
-               dal.Where(`_tool_jenkins_stages.connection_id = ? 
+               dal.Select("tjb.*"),
+               dal.From(`_tool_jenkins_builds tjb`),
+               dal.Join(`inner join _tool_jenkins_stages tjs 
+                                               on tjs.build_name = 
tjb.full_display_name 
+                                               and tjs.connection_id = 
tjb.connection_id`),
+               dal.Where(`tjb.connection_id = ? 
                                                        and tjb.job_path = ? 
and tjb.job_name = ?`,
                        data.Options.ConnectionId, data.Options.JobPath, 
data.Options.JobName),
-               dal.Groupby("_tool_jenkins_stages.build_name"),
        }
        cursor, err := db.Cursor(clauses...)
        if err != nil {
@@ -58,35 +53,17 @@ func EnrichApiBuildWithStages(taskCtx core.SubTaskContext) 
errors.Error {
        taskCtx.SetProgress(0, -1)
 
        for cursor.Next() {
-               var buildName string
-               err = errors.Convert(cursor.Scan(&buildName))
-               if err != nil {
-                       return err
-               }
-               if buildName == "" {
-                       continue
-               }
                build := &models.JenkinsBuild{}
-               build.ConnectionId = data.Options.ConnectionId
-               str := strings.Split(buildName, "#")
-               build.JobName = strings.TrimSpace(str[0])
-               var number int
-               number, err = 
errors.Convert01(strconv.Atoi(strings.TrimSpace(str[1])))
+               err = db.Fetch(cursor, build)
                if err != nil {
                        return err
                }
-               build.Number = int64(number)
-               err = db.First(build)
-               if err != nil {
-                       return errors.Convert(err)
-               }
                build.HasStages = true
-
-               err = db.Update(build)
+               err = db.CreateOrUpdate(build)
                if err != nil {
-                       return errors.Convert(err)
+                       return err
                }
-               taskCtx.IncProgress(1)
        }
+
        return nil
 }
diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv 
b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
index 019b75cdb..6f48b125c 100644
--- a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
+++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
@@ -1,2 +1,2 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""sprint"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2020-06-05"",""end"":""2021-12-04"",""realBegan"":null,""realEnd"":null,""days
 [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""sprint"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2020-06-05"",""end"":""2021-12-04"",""realBegan"":null,""realEnd"":null,""days
 [...]
diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv 
b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
index e58feada0..ccfdbc5d5 100644
--- a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
+++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
@@ -1,2 +1,2 @@
 id,params,data,url,input,created_at
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan
 
style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":""
 [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan
 
style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":""
 [...]
diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv 
b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
index 8aa6a4489..73a83d8f6 100644
--- a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
+++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
@@ -1,4 +1,4 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""
 [...]
-2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""
 [...]
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""
 [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""
 [...]
+2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""
 [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""
 [...]
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv 
b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
index da82aa928..1f49d1a4c 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
@@ -1,4 +1,4 @@
-connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,fee
 [...]
+connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,fee
 [...]
 1,1,7,3,0,0,0,8,1,0,1,1,1,0,0,首页页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入首页</p>
 <p>[结果]出现乱码&nbsp;&nbsp;&nbsp;&nbsp;</p>
 
<p>[期望]正常显示</p>",active,,,0,0,,,,7,测试甲,2012-06-05T02:56:11.000+00:00,主干,4,开发甲,2012-06-05T02:56:11.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv 
b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
index 05762df6f..74ccea041 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
@@ -1,2 +1,2 @@
 
connection_id,id,project,model,type,lifetime,budget,budget_unit,attribute,percent,milestone,output,auth,parent,path,grade,name,code,plan_begin,plan_end,real_began,real_end,days,status,sub_status,pri,description,version,parent_version,plan_duration,real_duration,opened_by_id,opened_date,opened_version,last_edited_by_id,last_edited_date,closed_by_id,closed_date,canceled_by_id,canceled_date,suspended_date,po_id,pm_id,qd_id,rd_id,team,acl,order_in,vision,display_cards,fluid_board,deleted,tot
 [...]
-1,1,7,,sprint,sprint,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2020-06-05,2021-12-04,,,391,doing,,1,开发企业网站的基本雏形。<br
 
/>,0,0,0,0,0,,,0,,0,,0,,0000-00-00,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,40,29,0,58,0
+1,1,7,,sprint,sprint,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2020-06-05,2021-12-04,,,391,doing,,1,开发企业网站的基本雏形。<br
 
/>,0,0,0,0,0,,,0,,0,,0,,0000-00-00,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,40,29,0,58.921,0
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv 
b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv
index b0bd40c9c..eff1a9f33 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv
@@ -1,2 +1,2 @@
 
connection_id,id,program,name,code,bind,line,type,status,sub_status,description,po_id,qd_id,rd_id,acl,reviewer,created_by_id,created_date,created_version,order_in,deleted,plans,releases,builds,cases,projects,executions,bugs,docs,progress,case_review
-1,3,10,产品名称1,产品代号2,0,31,normal,normal,,"<span 
style=""background-color:#FFFFFF;"">产品描述1</span>",1,1,1,private,"devlake,dev1",1,2022-11-17T06:42:25.000+00:00,17.6,15,0,1,0,0,0,0,0,0,0,0,0
+1,3,10,产品名称1,产品代号2,0,31,normal,normal,,"<span 
style=""background-color:#FFFFFF;"">产品描述1</span>",1,1,1,private,"devlake,dev1",1,2022-11-17T06:42:25.000+00:00,17.6,15,0,1,0,0,0,0,0,0,0,12.121,0
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv 
b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
index 276d53cda..4125a08dc 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
@@ -1,4 +1,4 @@
-connection_id,id,execution_id,project,parent,execution,module,design,story,story_version,design_version,from_bug,feedback,from_issue,name,type,mode,pri,estimate,consumed,deadline,status,sub_status,color,description,version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,est_started,real_started,finished_id,finished_date,finished_list,canceled_id,canceled_date,closed_by_id,closed_date,plan_duration,real_duration,closed_reason,last_edited_id,last_edite
 [...]
-1,1,1,13,0,9,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> 
</span><br /><div><br 
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,0000-00-00
 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,0
-1,2,1,13,0,9,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> 
</span><br /><div><br 
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,0000-00-00
 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,0
-1,3,1,13,0,9,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> 
</span><br /><div><br 
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,0000-00-00
 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,0
+connection_id,id,execution_id,project,parent,execution,module,design,story,story_version,design_version,from_bug,feedback,from_issue,name,type,mode,pri,estimate,consumed,deadline,status,sub_status,color,description,version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,est_started,real_started,finished_id,finished_date,finished_list,canceled_id,canceled_date,closed_by_id,closed_date,plan_duration,real_duration,closed_reason,last_edited_id,last_edite
 [...]
+1,1,1,13,0,9,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> 
</span><br /><div><br 
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,0000-00-00
 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11
+1,2,1,13,0,9,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> 
</span><br /><div><br 
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,0000-00-00
 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3
+1,3,1,13,0,9,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> 
</span><br /><div><br 
/></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,0000-00-00
 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121
diff --git a/plugins/zentao/models/archived/bug.go 
b/plugins/zentao/models/archived/bug.go
index a316cd809..84dd06945 100644
--- a/plugins/zentao/models/archived/bug.go
+++ b/plugins/zentao/models/archived/bug.go
@@ -78,7 +78,7 @@ type ZentaoBug struct {
        Repo           int        `json:"repo"`
        Mr             int        `json:"mr"`
        Entry          string     `json:"entry"`
-       Lines          string     `json:"lines"`
+       NumOfLine      string     `json:"lines"`
        V1             string     `json:"v1"`
        V2             string     `json:"v2"`
        RepoType       string     `json:"repoType"`
diff --git a/plugins/zentao/models/archived/execution.go 
b/plugins/zentao/models/archived/execution.go
index cca92bd95..5124b6473 100644
--- a/plugins/zentao/models/archived/execution.go
+++ b/plugins/zentao/models/archived/execution.go
@@ -80,8 +80,8 @@ type ZentaoExecution struct {
        TotalConsumed  int    `json:"totalConsumed"`
        TotalLeft      int    `json:"totalLeft"`
        ProjectId      uint64
-       Progress       int  `json:"progress"`
-       CaseReview     bool `json:"caseReview"`
+       Progress       float64 `json:"progress"`
+       CaseReview     bool    `json:"caseReview"`
        archived.NoPKModel
 }
 
diff --git a/plugins/zentao/models/archived/product.go 
b/plugins/zentao/models/archived/product.go
index c6977a099..f99872a7a 100644
--- a/plugins/zentao/models/archived/product.go
+++ b/plugins/zentao/models/archived/product.go
@@ -52,7 +52,7 @@ type ZentaoProduct struct {
        Executions     int        `json:"executions"`
        Bugs           int        `json:"bugs"`
        Docs           int        `json:"docs"`
-       Progress       int        `json:"progress"`
+       Progress       float64    `json:"progress"`
        CaseReview     bool       `json:"caseReview"`
        archived.NoPKModel
 }
diff --git a/plugins/zentao/models/archived/project.go 
b/plugins/zentao/models/archived/project.go
index 8d884420b..b6f93d6b4 100644
--- a/plugins/zentao/models/archived/project.go
+++ b/plugins/zentao/models/archived/project.go
@@ -82,11 +82,11 @@ type ZentaoProject struct {
        TeamCount      int    `json:"teamCount"`
        LeftTasks      string `json:"leftTasks"`
        //TeamMembers   []interface{} `json:"teamMembers" gorm:"-"`
-       TotalEstimate int `json:"totalEstimate"`
-       TotalConsumed int `json:"totalConsumed"`
-       TotalLeft     int `json:"totalLeft"`
-       Progress      int `json:"progress"`
-       TotalReal     int `json:"totalReal"`
+       TotalEstimate int     `json:"totalEstimate"`
+       TotalConsumed int     `json:"totalConsumed"`
+       TotalLeft     int     `json:"totalLeft"`
+       Progress      float64 `json:"progress"`
+       TotalReal     int     `json:"totalReal"`
 }
 type PM struct {
        PmId       uint64 `json:"id"`
@@ -101,11 +101,11 @@ type Whitelist []struct {
        WhitelistRealname string `json:"realname"`
 }
 type Hours struct {
-       HoursTotalEstimate int `json:"totalEstimate"`
-       HoursTotalConsumed int `json:"totalConsumed"`
-       HoursTotalLeft     int `json:"totalLeft"`
-       HoursProgress      int `json:"progress"`
-       HoursTotalReal     int `json:"totalReal"`
+       HoursTotalEstimate int     `json:"totalEstimate"`
+       HoursTotalConsumed int     `json:"totalConsumed"`
+       HoursTotalLeft     int     `json:"totalLeft"`
+       HoursProgress      float64 `json:"progress"`
+       HoursTotalReal     int     `json:"totalReal"`
 }
 
 func (ZentaoProject) TableName() string {
diff --git a/plugins/zentao/models/archived/task.go 
b/plugins/zentao/models/archived/task.go
index 4fd3533d3..0ee2d0568 100644
--- a/plugins/zentao/models/archived/task.go
+++ b/plugins/zentao/models/archived/task.go
@@ -76,7 +76,7 @@ type ZentaoTask struct {
        Repo               int        `json:"repo"`
        Mr                 int        `json:"mr"`
        Entry              string     `json:"entry"`
-       Lines              string     `json:"lines"`
+       NumOfLine          string     `json:"lines"`
        V1                 string     `json:"v1"`
        V2                 string     `json:"v2"`
        Deleted            bool       `json:"deleted"`
@@ -89,7 +89,7 @@ type ZentaoTask struct {
        AssignedToRealName string     `json:"assignedToRealName"`
        PriOrder           string     `json:"priOrder"`
        NeedConfirm        bool       `json:"needConfirm"`
-       Progress           int        `json:"progress"`
+       Progress           float64    `json:"progress"`
 }
 
 func (ZentaoTask) TableName() string {
diff --git a/plugins/zentao/models/bug.go b/plugins/zentao/models/bug.go
index ed0d65599..a04fedac6 100644
--- a/plugins/zentao/models/bug.go
+++ b/plugins/zentao/models/bug.go
@@ -94,7 +94,7 @@ type ZentaoBugRes struct {
        Repo         int        `json:"repo"`
        Mr           int        `json:"mr"`
        Entry        string     `json:"entry"`
-       Lines        string     `json:"lines"`
+       NumOfLine    string     `json:"lines"`
        V1           string     `json:"v1"`
        V2           string     `json:"v2"`
        RepoType     string     `json:"repoType"`
@@ -171,7 +171,7 @@ type ZentaoBug struct {
        Repo           int        `json:"repo"`
        Mr             int        `json:"mr"`
        Entry          string     `json:"entry"`
-       Lines          string     `json:"lines"`
+       NumOfLine      string     `json:"lines"`
        V1             string     `json:"v1"`
        V2             string     `json:"v2"`
        RepoType       string     `json:"repoType"`
diff --git a/plugins/zentao/models/execution.go 
b/plugins/zentao/models/execution.go
index 38768ae96..f58d97df4 100644
--- a/plugins/zentao/models/execution.go
+++ b/plugins/zentao/models/execution.go
@@ -178,7 +178,7 @@ type ZentaoExecutionRes struct {
                FluidBoard     string `json:"fluidBoard"`
                Deleted        string `json:"deleted"`
        } `json:"projectInfo"`
-       Progress    int `json:"progress"`
+       Progress    float64 `json:"progress"`
        TeamMembers []struct {
                ID         uint64 `json:"id"`
                Root       int    `json:"root"`
@@ -207,8 +207,8 @@ type ZentaoExecutionRes struct {
 }
 
 type ZentaoExecution struct {
-       ConnectionId   uint64     `gorm:"primaryKey"`
-       Id             uint64     `json:"id" gorm:"primaryKey"`
+       ConnectionId   uint64     `gorm:"primaryKey;type:BIGINT  NOT NULL"`
+       Id             uint64     `json:"id" gorm:"primaryKey;type:BIGINT  NOT 
NULL"`
        Project        uint64     `json:"project"`
        Model          string     `json:"model"`
        Type           string     `json:"type"`
@@ -264,8 +264,8 @@ type ZentaoExecution struct {
        TotalConsumed  int    `json:"totalConsumed"`
        TotalLeft      int    `json:"totalLeft"`
        ProjectId      uint64
-       Progress       int  `json:"progress"`
-       CaseReview     bool `json:"caseReview"`
+       Progress       float64 `json:"progress"`
+       CaseReview     bool    `json:"caseReview"`
        common.NoPKModel
 }
 
diff --git a/plugins/zentao/models/product.go b/plugins/zentao/models/product.go
index 802e8668d..ef5cd8418 100644
--- a/plugins/zentao/models/product.go
+++ b/plugins/zentao/models/product.go
@@ -74,21 +74,21 @@ type ZentaoProductRes struct {
                Closed    int `json:"closed"`
                Changing  int `json:"changing"`
        } `json:"stories"`
-       Plans      int  `json:"plans"`
-       Releases   int  `json:"releases"`
-       Builds     int  `json:"builds"`
-       Cases      int  `json:"cases"`
-       Projects   int  `json:"projects"`
-       Executions int  `json:"executions"`
-       Bugs       int  `json:"bugs"`
-       Docs       int  `json:"docs"`
-       Progress   int  `json:"progress"`
-       CaseReview bool `json:"caseReview"`
+       Plans      int     `json:"plans"`
+       Releases   int     `json:"releases"`
+       Builds     int     `json:"builds"`
+       Cases      int     `json:"cases"`
+       Projects   int     `json:"projects"`
+       Executions int     `json:"executions"`
+       Bugs       int     `json:"bugs"`
+       Docs       int     `json:"docs"`
+       Progress   float64 `json:"progress"`
+       CaseReview bool    `json:"caseReview"`
 }
 
 type ZentaoProduct struct {
-       ConnectionId   uint64 `gorm:"primaryKey"`
-       Id             uint64 `json:"id" gorm:"primaryKey"`
+       ConnectionId   uint64 `gorm:"primaryKey;type:BIGINT  NOT NULL"`
+       Id             uint64 `json:"id" gorm:"primaryKey;type:BIGINT  NOT 
NULL"`
        Program        int    `json:"program"`
        Name           string `json:"name"`
        Code           string `json:"code"`
@@ -116,7 +116,7 @@ type ZentaoProduct struct {
        Executions     int        `json:"executions"`
        Bugs           int        `json:"bugs"`
        Docs           int        `json:"docs"`
-       Progress       int        `json:"progress"`
+       Progress       float64    `json:"progress"`
        CaseReview     bool       `json:"caseReview"`
        common.NoPKModel
 }
diff --git a/plugins/zentao/models/project.go b/plugins/zentao/models/project.go
index 08238a8a5..1d6627a1a 100644
--- a/plugins/zentao/models/project.go
+++ b/plugins/zentao/models/project.go
@@ -82,11 +82,11 @@ type ZentaoProject struct {
        TeamCount      int    `json:"teamCount"`
        LeftTasks      string `json:"leftTasks"`
        //TeamMembers   []interface{} `json:"teamMembers" gorm:"-"`
-       TotalEstimate int `json:"totalEstimate"`
-       TotalConsumed int `json:"totalConsumed"`
-       TotalLeft     int `json:"totalLeft"`
-       Progress      int `json:"progress"`
-       TotalReal     int `json:"totalReal"`
+       TotalEstimate int     `json:"totalEstimate"`
+       TotalConsumed int     `json:"totalConsumed"`
+       TotalLeft     int     `json:"totalLeft"`
+       Progress      float64 `json:"progress"`
+       TotalReal     int     `json:"totalReal"`
 }
 type PM struct {
        PmId       uint64 `json:"id"`
diff --git a/plugins/zentao/models/task.go b/plugins/zentao/models/task.go
index 4ca8777c5..228e38a7a 100644
--- a/plugins/zentao/models/task.go
+++ b/plugins/zentao/models/task.go
@@ -106,7 +106,7 @@ type ZentaoTaskRes struct {
        Repo           int        `json:"repo"`
        Mr             int        `json:"mr"`
        Entry          string     `json:"entry"`
-       Lines          string     `json:"lines"`
+       NumOfLine      string     `json:"lines"`
        V1             string     `json:"v1"`
        V2             string     `json:"v2"`
        Deleted        bool       `json:"deleted"`
@@ -119,11 +119,11 @@ type ZentaoTaskRes struct {
        } `json:"latestStoryVersion"`
        StoryStatus interface {
        } `json:"storyStatus"`
-       AssignedToRealName string `json:"assignedToRealName"`
-       PriOrder           string `json:"priOrder"`
-       Delay              int    `json:"delay"`
-       NeedConfirm        bool   `json:"needConfirm"`
-       Progress           int    `json:"progress"`
+       AssignedToRealName string  `json:"assignedToRealName"`
+       PriOrder           string  `json:"priOrder"`
+       Delay              int     `json:"delay"`
+       NeedConfirm        bool    `json:"needConfirm"`
+       Progress           float64 `json:"progress"`
 }
 
 type ZentaoTask struct {
@@ -180,7 +180,7 @@ type ZentaoTask struct {
        Repo               int        `json:"repo"`
        Mr                 int        `json:"mr"`
        Entry              string     `json:"entry"`
-       Lines              string     `json:"lines"`
+       NumOfLine          string     `json:"lines"`
        V1                 string     `json:"v1"`
        V2                 string     `json:"v2"`
        Deleted            bool       `json:"deleted"`
@@ -193,7 +193,7 @@ type ZentaoTask struct {
        AssignedToRealName string     `json:"assignedToRealName"`
        PriOrder           string     `json:"priOrder"`
        NeedConfirm        bool       `json:"needConfirm"`
-       Progress           int        `json:"progress"`
+       Progress           float64    `json:"progress"`
 }
 
 func (ZentaoTask) TableName() string {
diff --git a/plugins/zentao/tasks/bug_extractor.go 
b/plugins/zentao/tasks/bug_extractor.go
index 088c75602..085e2a342 100644
--- a/plugins/zentao/tasks/bug_extractor.go
+++ b/plugins/zentao/tasks/bug_extractor.go
@@ -109,7 +109,7 @@ func ExtractBug(taskCtx core.SubTaskContext) errors.Error {
                                Repo:           res.Repo,
                                Mr:             res.Mr,
                                Entry:          res.Entry,
-                               Lines:          res.Lines,
+                               NumOfLine:      res.NumOfLine,
                                V1:             res.V1,
                                V2:             res.V2,
                                RepoType:       res.RepoType,
diff --git a/plugins/zentao/tasks/task_extractor.go 
b/plugins/zentao/tasks/task_extractor.go
index d736bc008..7fea9f0d6 100644
--- a/plugins/zentao/tasks/task_extractor.go
+++ b/plugins/zentao/tasks/task_extractor.go
@@ -106,7 +106,7 @@ func ExtractTask(taskCtx core.SubTaskContext) errors.Error {
                                Repo:               res.Repo,
                                Mr:                 res.Mr,
                                Entry:              res.Entry,
-                               Lines:              res.Lines,
+                               NumOfLine:          res.NumOfLine,
                                V1:                 res.V1,
                                V2:                 res.V2,
                                Deleted:            res.Deleted,

Reply via email to