Skip to content

Commit 054bf5d

Browse files
committed
feat: refine code
1 parent dcbc1b7 commit 054bf5d

File tree

2 files changed

+63
-64
lines changed

2 files changed

+63
-64
lines changed

backend/plugins/dora/tasks/change_lead_time_calculator.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,6 @@ func getDeploymentCommit(mergeSha string, projectName string, db dal.Dal) (*devo
233233
return deploymentCommits[0], nil
234234
}
235235

236-
// CalculateInProgressToDoneLeadTime is a placeholder function to calculate lead time.
237-
// TODO: Implement the actual calculation logic.
238-
func CalculateInProgressToDoneLeadTime(taskCtx plugin.SubTaskContext) errors.Error {
239-
logger := taskCtx.GetLogger()
240-
logger.Info("Placeholder function CalculateInProgressToDoneLeadTime called.")
241-
// Later, add logic here to calculate lead time from 'in progress' to 'done' statuses for issues/tickets.
242-
return nil // Return nil for now, indicating success
243-
}
244-
245236
func computeTimeSpan(start, end *time.Time) *int64 {
246237
if start == nil || end == nil {
247238
return nil

backend/plugins/dora/tasks/issue_lead_time_calculator.go

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
jiraModels "github.com/apache/incubator-devlake/plugins/jira/models"
1313
)
1414

15+
// CalculateIssueLeadTimeMeta contains metadata for the CalculateIssueLeadTime subtask.
1516
var CalculateIssueLeadTimeMeta = plugin.SubTaskMeta{
1617
Name: "calculateIssueLeadTime",
1718
EntryPoint: CalculateIssueLeadTime,
@@ -20,118 +21,125 @@ var CalculateIssueLeadTimeMeta = plugin.SubTaskMeta{
2021
DomainTypes: []string{plugin.DOMAIN_TYPE_TICKET},
2122
}
2223

24+
// CalculateIssueLeadTime calculates the lead time for issues from first 'In Progress' status to 'Done' status.
2325
func CalculateIssueLeadTime(taskCtx plugin.SubTaskContext) errors.Error {
2426
db := taskCtx.GetDal()
27+
logger := taskCtx.GetLogger()
2528
data := taskCtx.GetData().(*DoraTaskData)
2629

27-
logger := taskCtx.GetLogger()
28-
logger.Info(fmt.Sprintf("Starting calculateIssueLeadTime task for project %s", data.Options.ProjectName))
30+
logger.Info("Starting calculateIssueLeadTime task for project %s", data.Options.ProjectName)
2931

30-
// 1) delete any old metrics for this project
31-
if err := db.Delete(
32+
// Delete any old metrics for this project
33+
err := db.Delete(
3234
&models.IssueLeadTimeMetric{},
3335
dal.Where("project_name = ?", data.Options.ProjectName),
34-
); err != nil {
35-
return errors.Default.Wrap(err, "deleting old issue lead time metrics")
36+
)
37+
if err != nil {
38+
return errors.Default.Wrap(err, "failed to delete old issue lead time metrics")
3639
}
37-
logger.Info(fmt.Sprintf("Deleted old issue lead time metrics for project %s", data.Options.ProjectName))
40+
logger.Info("Deleted old issue lead time metrics for project %s", data.Options.ProjectName)
3841

39-
// 2) get the actual _tool_jira_* table names
40-
rawItems := jiraModels.JiraIssueChangelogItems{}.TableName() // "_tool_jira_issue_changelog_items"
41-
rawChgs := jiraModels.JiraIssueChangelogs{}.TableName() // "_tool_jira_issue_changelogs"
42-
rawIss := jiraModels.JiraIssue{}.TableName() // "_tool_jira_issues"
42+
// Get the actual _tool_jira_* table names
43+
rawItems := jiraModels.JiraIssueChangelogItems{}.TableName()
44+
rawChgs := jiraModels.JiraIssueChangelogs{}.TableName()
45+
rawIss := jiraModels.JiraIssue{}.TableName()
4346

44-
// 3) build the SQL query, filter out null timestamps and use only latest resolution per issue
45-
query := `
47+
// Build the SQL query, filter out null timestamps and use only latest resolution per issue
48+
query := fmt.Sprintf(`
4649
SELECT
47-
c.issue_id AS issue_id,
48-
MIN(CASE WHEN UPPER(TRIM(i.to_string)) IN ('IN PROGRESS', 'INPROGRESS') THEN c.created END) AS in_progress_timestamp,
49-
u.resolution_date AS done_timestamp
50-
FROM ` + rawItems + ` i
51-
JOIN ` + rawChgs + ` c
52-
ON i.connection_id = c.connection_id
53-
AND i.changelog_id = c.changelog_id
54-
JOIN ` + rawIss + ` u
55-
ON c.connection_id = u.connection_id
56-
AND c.issue_id = u.issue_id
50+
c.issue_id AS issue_id,
51+
MIN(CASE WHEN UPPER(TRIM(i.to_string)) IN ('IN PROGRESS', 'INPROGRESS') THEN c.created END) AS in_progress_timestamp,
52+
u.resolution_date AS done_timestamp
53+
FROM %s i
54+
JOIN %s c
55+
ON i.connection_id = c.connection_id
56+
AND i.changelog_id = c.changelog_id
57+
JOIN %s u
58+
ON c.connection_id = u.connection_id
59+
AND c.issue_id = u.issue_id
5760
JOIN _tool_jira_board_issues bi
58-
ON u.connection_id = bi.connection_id
59-
AND u.issue_id = bi.issue_id
61+
ON u.connection_id = bi.connection_id
62+
AND u.issue_id = bi.issue_id
6063
JOIN project_mapping pm
61-
ON pm.row_id = CONCAT('jira:JiraBoard:', bi.connection_id, ':', bi.board_id)
62-
AND pm.table = 'boards'
64+
ON pm.row_id = CONCAT('jira:JiraBoard:', bi.connection_id, ':', bi.board_id)
65+
AND pm.table = 'boards'
6366
WHERE i.field = 'status'
64-
AND pm.project_name = ?
65-
AND u.resolution_date IS NOT NULL
66-
AND u.resolution_date = (
67-
SELECT MAX(u2.resolution_date)
68-
FROM ` + rawIss + ` u2
69-
WHERE u2.connection_id = u.connection_id
70-
AND u2.issue_id = u.issue_id
71-
)
67+
AND pm.project_name = ?
68+
AND u.resolution_date IS NOT NULL
69+
AND u.resolution_date = (
70+
SELECT MAX(u2.resolution_date)
71+
FROM %s u2
72+
WHERE u2.connection_id = u.connection_id
73+
AND u2.issue_id = u.issue_id
74+
)
7275
GROUP BY c.issue_id, u.resolution_date
7376
HAVING in_progress_timestamp IS NOT NULL
74-
`
75-
logger.Info(fmt.Sprintf("Executing SQL query for DevLake project: %s", data.Options.ProjectName))
77+
`, rawItems, rawChgs, rawIss, rawIss)
7678

77-
// 4) execute & stream
79+
logger.Info("Executing SQL query for DevLake project: %s", data.Options.ProjectName)
80+
81+
// Execute query and stream results
7882
rows, err := db.RawCursor(query, data.Options.ProjectName)
7983
if err != nil {
80-
logger.Error(err, "")
81-
return errors.Default.Wrap(err, "running lead time aggregation query")
84+
return errors.Default.Wrap(err, "failed to run lead time aggregation query")
8285
}
8386
defer rows.Close()
8487

8588
rowCount := 0
8689
logger.Info("Starting to process SQL query results...")
90+
8791
for rows.Next() {
8892
var (
8993
rawIssueID uint64
9094
rawInProgress sql.NullTime
9195
rawDone sql.NullTime
9296
)
97+
9398
if scanErr := rows.Scan(&rawIssueID, &rawInProgress, &rawDone); scanErr != nil {
94-
logger.Error(scanErr, "")
95-
return errors.Default.Wrap(scanErr, "scanning lead time row")
99+
return errors.Default.Wrap(scanErr, "failed to scan lead time row")
96100
}
97-
logger.Info(fmt.Sprintf("Scanned row: issueID=%d, inProgress=%v, done=%v", rawIssueID, rawInProgress, rawDone))
98-
// skip if null
101+
102+
logger.Debug("Scanned row: issueID=%d, inProgress=%v, done=%v", rawIssueID, rawInProgress, rawDone)
103+
104+
// Skip if null timestamps
99105
if !rawInProgress.Valid || !rawDone.Valid {
100-
logger.Debug(fmt.Sprintf("Skipping row with null timestamp: issueID=%d", rawIssueID))
106+
logger.Debug("Skipping row with null timestamp: issueID=%d", rawIssueID)
101107
continue
102108
}
109+
103110
start := rawInProgress.Time
104111
end := rawDone.Time
105112
mins := int64(end.Sub(start).Minutes())
113+
114+
// Skip negative lead times
106115
if mins < 0 {
107-
logger.Info(fmt.Sprintf("Skipping row with negative lead time: issueID=%d", rawIssueID))
116+
logger.Debug("Skipping row with negative lead time: issueID=%d", rawIssueID)
108117
continue
109118
}
110119

111-
// 5) upsert
120+
// Create and save the metric
112121
metric := &models.IssueLeadTimeMetric{
113122
ProjectName: data.Options.ProjectName,
114123
IssueId: strconv.FormatUint(rawIssueID, 10),
115124
InProgressDate: &start,
116125
DoneDate: &end,
117126
InProgressToDoneMinutes: &mins,
118127
}
119-
logger.Info(fmt.Sprintf("About to upsert metric: projectName=%s, issueId=%s, minutes=%d",
120-
metric.ProjectName, metric.IssueId, *metric.InProgressToDoneMinutes))
128+
129+
logger.Debug("Upserting metric: projectName=%s, issueId=%s, minutes=%d",
130+
metric.ProjectName, metric.IssueId, *metric.InProgressToDoneMinutes)
121131

122132
if upsertErr := db.CreateOrUpdate(metric); upsertErr != nil {
123-
logger.Error(upsertErr, fmt.Sprintf("Failed to upsert metric for issueId=%s", metric.IssueId))
124-
return errors.Default.Wrap(upsertErr, "upserting issue lead time metric")
133+
return errors.Default.Wrap(upsertErr, "failed to upsert issue lead time metric")
125134
}
126-
logger.Info(fmt.Sprintf("Successfully upserted metric for issueId=%s", metric.IssueId))
135+
127136
rowCount++
128137
}
129138

130-
logger.Info(fmt.Sprintf("Completed calculateIssueLeadTime task: processed %d records", rowCount))
139+
logger.Info("Completed calculateIssueLeadTime task: processed %d records", rowCount)
131140

132141
if err := rows.Err(); err != nil && err != sql.ErrNoRows {
133-
logger.Error(err, "")
134-
return errors.Default.Wrap(err, "iterating lead time rows")
142+
return errors.Default.Wrap(err, "error iterating lead time rows")
135143
}
136144

137145
return nil

0 commit comments

Comments
 (0)