From a2e048defb81a347407c016d4523e6b52e2d1bc3 Mon Sep 17 00:00:00 2001 From: chengjoey <30427474+chengjoey@users.noreply.github.com> Date: Mon, 8 Nov 2021 14:37:09 +0800 Subject: [PATCH] issues import support estimate time (#2882) --- apistructs/issue.go | 34 ++++++++++++++++++++++ apistructs/issue_test.go | 13 +++++++++ modules/dop/services/issue/convert.go | 22 ++++++++++++-- modules/dop/services/issue/convert_test.go | 14 +++++---- modules/dop/services/issue/import.go | 15 ++++++++++ pkg/erda-configs/i18n/issue.json | 8 ++--- 6 files changed, 93 insertions(+), 13 deletions(-) diff --git a/apistructs/issue.go b/apistructs/issue.go index d22be192e07..fc91f725aa9 100644 --- a/apistructs/issue.go +++ b/apistructs/issue.go @@ -18,6 +18,7 @@ import ( "bytes" "encoding/json" "fmt" + "regexp" "strconv" "strings" "time" @@ -495,6 +496,39 @@ func (imh *IssueManHour) Convert2String() string { return string(mh) } +var estimateRegexp, _ = regexp.Compile("^[0-9]+[wdhm]+$") + +func NewManhour(manhour string) (IssueManHour, error) { + if manhour == "" { + return IssueManHour{}, nil + } + if !estimateRegexp.MatchString(manhour) { + return IssueManHour{}, fmt.Errorf("invalid estimate time: %s", manhour) + } + timeType := manhour[len(manhour)-1] + timeSet := manhour[:len(manhour)-1] + timeVal, err := strconv.ParseUint(timeSet, 10, 64) + if err != nil { + return IssueManHour{}, fmt.Errorf("invalid man hour: %s, err: %v", manhour, err) + } + switch timeType { + case 'm': + val := int64(timeVal) + return IssueManHour{EstimateTime: val, RemainingTime: val}, nil + case 'h': + val := int64(timeVal) * 60 + return IssueManHour{EstimateTime: val, RemainingTime: val}, nil + case 'd': + val := int64(timeVal) * 60 * 8 + return IssueManHour{EstimateTime: val, RemainingTime: val}, nil + case 'w': + val := int64(timeVal) * 60 * 8 * 5 + return IssueManHour{EstimateTime: val, RemainingTime: val}, nil + default: + return IssueManHour{}, fmt.Errorf("invalid man hour: %s", manhour) + } +} + // Clean get,list事件返回时 开始时间,工作内容都需要为空 func (imh *IssueManHour) Clean() IssueManHour { imh.ThisElapsedTime = 0 diff --git a/apistructs/issue_test.go b/apistructs/issue_test.go index 7595e691442..575852bddae 100644 --- a/apistructs/issue_test.go +++ b/apistructs/issue_test.go @@ -27,3 +27,16 @@ func TestSetRelatedIssueIDs(t *testing.T) { assert.Equal(t, uint64(1001), relatedIDs[0]) assert.Equal(t, uint64(1002), relatedIDs[1]) } + +func TestNewManhour(t *testing.T) { + _, err := NewManhour("2w3d") + assert.Equal(t, true, err != nil) + est1, _ := NewManhour("3h") + assert.Equal(t, int64(180), est1.EstimateTime) + est2, _ := NewManhour("9m") + assert.Equal(t, int64(9), est2.EstimateTime) + est3, _ := NewManhour("5d") + assert.Equal(t, int64(2400), est3.EstimateTime) + est4, _ := NewManhour("1w") + assert.Equal(t, int64(2400), est4.EstimateTime) +} diff --git a/modules/dop/services/issue/convert.go b/modules/dop/services/issue/convert.go index d8826f6074c..0f5e82a67b2 100644 --- a/modules/dop/services/issue/convert.go +++ b/modules/dop/services/issue/convert.go @@ -269,6 +269,10 @@ func (svc *Issue) convertIssueToExcelList(issues []apistructs.Issue, property [] Type: i.Type, Value: i.GetStage(), } + finishTime := "" + if i.FinishTime != nil { + finishTime = i.FinishTime.Format("2006-01-02 15:04:05") + } _, relatedIssueIDs, err := svc.issueRelated.GetIssueRelationsByIssueIDs(uint64(i.ID)) if err != nil { @@ -318,6 +322,7 @@ func (svc *Issue) convertIssueToExcelList(issues []apistructs.Issue, property [] i.CreatedAt.Format("2006-01-02 15:04:05"), strings.Join(relatedIssueIDStrs, ","), i.ManHour.GetFormartTime("EstimateTime"), + finishTime, })) relations := propertyMap[i.ID] // 获取每个自定义字段的值 @@ -478,15 +483,26 @@ func (svc *Issue) decodeFromExcelFile(req apistructs.IssueImportExcelRequest, r falseReason = append(falseReason, fmt.Sprintf("failed to convert related issue ids: %s, err: %v", row[17], err)) continue } - // row[17] EstimateTime, jump over + // row[17] EstimateTime + if row[17] != "" { + manHour, err := apistructs.NewManhour(row[17]) + if err != nil { + falseExcel = append(falseExcel, i+1) + falseReason = append(falseReason, fmt.Sprintf("failed to counvert estimate time: %s, err: %v", row[17], err)) + continue + } + issue.ManHour = manHour + } + + // row[18] finish time, jump over // 获取自定义字段 relation := apistructs.IssuePropertyRelationCreateRequest{ OrgID: req.OrgID, ProjectID: int64(req.ProjectID), } - for indexx, line := range row[18:] { - index := indexx + 18 + for indexx, line := range row[19:] { + index := indexx + 19 // 获取字段名对应的字段 instance := apistructs.IssuePropertyInstance{ IssuePropertyIndex: propertyNameMap[rows[0][index]], diff --git a/modules/dop/services/issue/convert_test.go b/modules/dop/services/issue/convert_test.go index ca161f85be2..7249b07eaa9 100644 --- a/modules/dop/services/issue/convert_test.go +++ b/modules/dop/services/issue/convert_test.go @@ -19,6 +19,7 @@ import ( "reflect" "strings" "testing" + "time" "bou.ke/monkey" "github.com/magiconair/properties/assert" @@ -50,11 +51,11 @@ func TestDecodeFromExcelFile(t *testing.T) { tm := monkey.Patch(excel.Decode, func(r io.Reader) ([][][]string, error) { return [][][]string{ [][]string{ - []string{"ID", "标题", "内容", "状态", "创建人", "处理人", "负责人", "任务类型或缺陷引入源", "优先级", "所属迭代", "复杂度", "严重程度", "标签", "类型", "截止时间", "创建时间", "被以下事项关联", "预估时间"}, - []string{"1", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "", "", "", ""}, - []string{"a", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "", "", "", ""}, - []string{"2", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "a,b", "", "", ""}, - []string{"2", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "", "", "", ""}, + []string{"ID", "标题", "内容", "状态", "创建人", "处理人", "负责人", "任务类型或缺陷引入源", "优先级", "所属迭代", "复杂度", "严重程度", "标签", "类型", "截止时间", "创建时间", "被以下事项关联", "预估时间", "关闭时间", ""}, + []string{"1", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "", "", "", "", "", ""}, + []string{"a", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "", "", "", "", "", ""}, + []string{"2", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "a,b", "", "", "3h", "", ""}, + []string{"2", "erda", "erda", "待处理", "erda", "erda", "erda", "缺陷", "低", "1.1", "中", "一般", "", "缺陷", "", "2021-09-26 15:19:00", "", "", "", "3d", "", ""}, }, }, nil }) @@ -149,6 +150,7 @@ func TestConvertIssueToExcelList(t *testing.T) { defer p5.Unpatch() svc := New(WithDBClient(db), WithIssueRelated(related), WithBundle(bdl)) - _, err := svc.convertIssueToExcelList([]apistructs.Issue{}, []apistructs.IssuePropertyIndex{}, 1, false, map[issueStage]string{}, "cn") + finishTime := time.Now() + _, err := svc.convertIssueToExcelList([]apistructs.Issue{{ID: 1, FinishTime: &finishTime}}, []apistructs.IssuePropertyIndex{}, 1, false, map[issueStage]string{}, "cn") assert.Equal(t, err, nil) } diff --git a/modules/dop/services/issue/import.go b/modules/dop/services/issue/import.go index 6195ef6778b..feb28b3ba05 100644 --- a/modules/dop/services/issue/import.go +++ b/modules/dop/services/issue/import.go @@ -15,6 +15,7 @@ package issue import ( + "encoding/json" "fmt" "net/http" @@ -80,6 +81,16 @@ func (svc *Issue) storeExcel2DB(request apistructs.IssueImportExcelRequest, issu issue.Source = req.Source issue.Stage = req.GetStage() issue.Owner = memberMap[req.Owner] + if req.ManHour.EstimateTime > 0 { + var oldManHour apistructs.IssueManHour + json.Unmarshal([]byte(issue.ManHour), &oldManHour) + oldManHour.EstimateTime = req.ManHour.EstimateTime + if oldManHour.RemainingTime == 0 { + oldManHour.RemainingTime = oldManHour.EstimateTime + } + newManHour, _ := json.Marshal(oldManHour) + issue.ManHour = string(newManHour) + } if err := svc.db.UpdateIssueType(&issue); err != nil { falseIssue = append(falseIssue, excelIndex[index]) falseReason = append(falseReason, fmt.Sprintf("failed to update issue: %s, err: %v", issue.Title, err)) @@ -162,6 +173,10 @@ func (svc *Issue) storeExcel2DB(request apistructs.IssueImportExcelRequest, issu Owner: memberMap[req.Owner], //ManHour: req.GetDBManHour(), } + if req.ManHour.EstimateTime > 0 { + newManHour, _ := json.Marshal(req.ManHour) + create.ManHour = string(newManHour) + } if string(create.Type) != string(request.Type) { falseIssue = append(falseIssue, excelIndex[index]) falseReason = append(falseReason, "创建任务失败, err:事件类型不符合") diff --git a/pkg/erda-configs/i18n/issue.json b/pkg/erda-configs/i18n/issue.json index 6d1c926302c..6fe36a989f5 100644 --- a/pkg/erda-configs/i18n/issue.json +++ b/pkg/erda-configs/i18n/issue.json @@ -1,12 +1,12 @@ { "zh-CN": { - "IssueExportTitle": "ID,标题,内容,状态,创建人,处理人,负责人,任务类型或缺陷引入源,优先级,所属迭代,复杂度,严重程度,标签,类型,截止时间,创建时间,被以下事项关联,预估时间", - "IssueExportSample": ",样例标题,样例内容,待处理,2,2, ,设计,中,1.0,中,一般,,任务,2021-02-01 00:00:00,2021-01-01 09:30:42,," + "IssueExportTitle": "ID,标题,内容,状态,创建人,处理人,负责人,任务类型或缺陷引入源,优先级,所属迭代,复杂度,严重程度,标签,类型,截止时间,创建时间,被以下事项关联,预估时间,关闭时间", + "IssueExportSample": ",样例标题,样例内容,待处理,2,2, ,设计,中,1.0,中,一般,,任务,2021-02-01 00:00:00,2021-01-01 09:30:42,,," }, "en-US": { - "IssueExportTitle": "ID,Title,Content,State,Creator,Assignee,Director,Task Type,Priority,Iteration,Complexity,Severity,Label,Type,End Time,Created Time,Associated by the following,Estimate time", - "IssueExportSample": ",Sample Title,Sample Content,OPENING,2,2,,Design,Medium,1.0,Medium,Ordinary,,Task,2021-02-01 00:00:00,2021-01-01 09:30:42,," + "IssueExportTitle": "ID,Title,Content,State,Creator,Assignee,Director,Task Type,Priority,Iteration,Complexity,Severity,Label,Type,End Time,Created Time,Associated by the following,Estimate time,Closing time", + "IssueExportSample": ",Sample Title,Sample Content,OPENING,2,2,,Design,Medium,1.0,Medium,Ordinary,,Task,2021-02-01 00:00:00,2021-01-01 09:30:42,,," } } \ No newline at end of file