Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add requirement-task-overview scenario #3313

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion conf/dop/dop.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,17 @@ component-protocol.components.issue-gantt.filter:
component-protocol.components.issue-gantt.gantt:
component-protocol.components.issue-gantt.ganttContainer:
component-protocol.components.issue-gantt.topHead:
component-protocol.components.issue-gantt.issueAddButton:
component-protocol.components.issue-gantt.issueAddButton:

# requirement-task-overview
component-protocol.components.requirement-task-overview.page:
component-protocol.components.requirement-task-overview.distributePage:
component-protocol.components.requirement-task-overview.topFilter:
component-protocol.components.requirement-task-overview.container:
component-protocol.components.requirement-task-overview.chartBlock:
component-protocol.components.requirement-task-overview.simpleChart:
component-protocol.components.requirement-task-overview.info:
component-protocol.components.requirement-task-overview.burnoutChartFilter:
component-protocol.components.requirement-task-overview.burnoutChart:
component-protocol.components.requirement-task-overview.pageContent:
component-protocol.components.requirement-task-overview.tabs:
1 change: 1 addition & 0 deletions modules/dop/component-protocol/components/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-dashboard"
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-gantt"
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-manage"
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview"
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/scenes-import-record"
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/test-dashboard"
_ "github.com/erda-project/erda/modules/dop/component-protocol/components/test-report"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed 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 burnoutChart

import (
"context"
"encoding/json"

"github.com/erda-project/erda-infra/providers/component-protocol/cptype"
"github.com/erda-project/erda/modules/dop/dao"
"github.com/erda-project/erda/modules/openapi/component-protocol/components/base"
)

type BurnoutChart struct {
base.DefaultProvider

Type string `json:"type"`
Props Props `json:"props"`
Issues []dao.IssueItem `json:"-"`
}

type (
Props struct {
ChartType string `json:"chartType"`
Title string `json:"title"`
PureChart bool `json:"pureChart"`
Option Option `json:"option"`
}

Option struct {
XAxis XAxis `json:"xAxis"`
YAxis YAxis `json:"yAxis"`
Legend Legend `json:"legend"`
Tooltip map[string]interface{} `json:"tooltip"`
Series []Series `json:"series"`
}

XAxis struct {
Type string `json:"type"`
Data []string `json:"data"`
}

YAxis struct {
Type string `json:"type"`
AxisLine map[string]interface{} `json:"axisLine"`
AxisLabel map[string]interface{} `json:"axisLabel"`
}

Legend struct {
Show bool `json:"show"`
Bottom bool `json:"bottom"`
Data []string `json:"data"`
}

Series struct {
Data []int `json:"data"`
Name string `json:"name"`
Type string `json:"type"`
Smooth bool `json:"smooth"`
ItemStyle map[string]interface{} `json:"itemStyle"`
MarkLine MarkLine `json:"markLine"`
}

MarkLine struct {
Label map[string]interface{} `json:"label"`
LineStyle map[string]interface{} `json:"lineStyle"`
Data [][]Data `json:"data"`
}

Data struct {
Name string `json:"name"`
Coord []string `json:"coord"`
}
)

func (f *BurnoutChart) SetToProtocolComponent(c *cptype.Component) error {
b, err := json.Marshal(f)
if err != nil {
return err
}
return json.Unmarshal(b, &c)
}

func (f *BurnoutChart) InitFromProtocol(ctx context.Context, c *cptype.Component) error {
b, err := json.Marshal(c)
if err != nil {
return err
}
return json.Unmarshal(b, f)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed 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 burnoutChart

import (
"context"
"encoding/json"
"strconv"
"time"

"github.com/erda-project/erda-infra/base/servicehub"
"github.com/erda-project/erda-infra/providers/component-protocol/cptype"
"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common"
"github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper"
"github.com/erda-project/erda/modules/dop/dao"
"github.com/erda-project/erda/modules/openapi/component-protocol/components/base"
)

func init() {
base.InitProviderWithCreator(common.ScenarioKeyTestDashboard, "burnoutChart", func() servicehub.Provider {
return &BurnoutChart{}
})
}

func (f *BurnoutChart) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error {
if err := f.InitFromProtocol(ctx, c); err != nil {
return err
}

h := gshelper.NewGSHelper(gs)
f.Issues = h.GetIssueList()

dates := make([]time.Time, 0)
dateMap := make(map[time.Time]int)
itr := h.GetIteration()
if itr.StartedAt == nil || itr.FinishedAt == nil {
return nil
}
for rd := common.RangeDate(*itr.StartedAt, *itr.FinishedAt); ; {
date := rd()
if date.IsZero() {
break
}
dates = append(dates, date)
dateMap[date] = 0
}

issueCreateMap := make(map[time.Time][]dao.IssueItem, 0)
issueFinishMap := make(map[time.Time][]dao.IssueItem, 0)
for _, issue := range f.Issues {
if h.GetBurnoutChartType() == "requirement" &&
issue.Type != apistructs.IssueTypeRequirement {
continue
}
if h.GetBurnoutChartType() == "task" &&
issue.Type != apistructs.IssueTypeTask {
continue
}
issueCreateMap[common.DateTime(issue.CreatedAt)] = append(issueCreateMap[common.DateTime(issue.CreatedAt)], issue)
if issue.FinishTime != nil {
issueFinishMap[common.DateTime(*issue.FinishTime)] = append(issueFinishMap[common.DateTime(*issue.FinishTime)], issue)
}
}

cur := 0
sum := 0
for i, date := range dates {
if date.After(time.Now()) {
cur = i
break
}
if _, ok := issueCreateMap[date]; ok {
if h.GetBurnoutChartDimension() == "total" {
sum += len(issueCreateMap[date])
}
if h.GetBurnoutChartDimension() == "workTime" {
for _, issue := range issueCreateMap[date] {
if issue.ManHour != "" {
var manHour apistructs.IssueManHour
if err := json.Unmarshal([]byte(issue.ManHour), &manHour); err != nil {
return err
}
sum += int(manHour.EstimateTime) / 60
}
}
}
}

if _, ok := issueFinishMap[date]; ok {
if h.GetBurnoutChartDimension() == "total" {
sum -= len(issueFinishMap[date])
}
if h.GetBurnoutChartDimension() == "workTime" {
for _, issue := range issueFinishMap[date] {
if issue.ManHour != "" {
var manHour apistructs.IssueManHour
if err := json.Unmarshal([]byte(issue.ManHour), &manHour); err != nil {
return err
}
sum -= int(manHour.ThisElapsedTime) / 60
}
}
}
}
dateMap[date] = sum
}

f.Type = "Chart"
f.Props = Props{
ChartType: "line",
Title: "燃尽图",
PureChart: true,
Option: Option{
XAxis: XAxis{
Type: "category",
Data: func() []string {
ss := make([]string, 0, len(dates))
for _, v := range dates {
ss = append(ss, v.Format("01-02"))
}
return ss
}(),
},
YAxis: YAxis{
Type: "value",
AxisLine: map[string]interface{}{
"lineStyle": map[string]interface{}{
"color": "rgba(48,38,71,0.30)",
},
},
AxisLabel: map[string]interface{}{
"formatter": func() string {
if h.GetBurnoutChartDimension() == "total" {
return "{value} 个"
}
return "{value} h"
}(),
},
},
Legend: Legend{
Show: true,
Bottom: true,
Data: []string{"实际燃尽"},
},
Tooltip: map[string]interface{}{
"trigger": "axis",
},
Series: []Series{
{
Data: func() []int {
counts := make([]int, 0)
for _, v := range dates[:cur] {
counts = append(counts, dateMap[v])
}
return counts
}(),
Name: "实际燃尽",
Type: "line",
Smooth: false,
ItemStyle: map[string]interface{}{
"color": "#D84B65",
},
MarkLine: MarkLine{
Label: map[string]interface{}{"position": "middle"},
LineStyle: map[string]interface{}{
"color": "rgba(48,38,71,0.20)",
},
Data: [][]Data{
{
{
Name: "预设燃尽",
Coord: func() []string {
ss := make([]string, 0, 2)
ss = append(ss, dates[0].Format("01-02"), strconv.Itoa(dateMap[dates[0]]))
return ss
}(),
},
{
Coord: func() []string {
ss := make([]string, 0, 2)
ss = append(ss, dates[len(dates)-1].Format("01-02"), "0")
return ss
}(),
},
},
},
},
},
},
},
}

return f.SetToProtocolComponent(c)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed 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 burnoutChartFilter

import (
"context"
"encoding/json"

"github.com/erda-project/erda-infra/providers/component-protocol/cptype"
"github.com/erda-project/erda/modules/openapi/component-protocol/components/base"
"github.com/erda-project/erda/modules/openapi/component-protocol/components/filter"
)

type Filter struct {
filter.CommonFilter
base.DefaultProvider

State State `json:"state,omitempty"`
}

type State struct {
Conditions []filter.PropCondition `json:"conditions,omitempty"`
Values Values `json:"values,omitempty"`
}

type Values struct {
Type string `json:"type"`
Dimension string `json:"dimension"`
}

const OperationKeyFilter filter.OperationKey = "filter"
const OperationOwnerSelectMe filter.OperationKey = "ownerSelectMe"

func (f *Filter) SetToProtocolComponent(c *cptype.Component) error {
b, err := json.Marshal(f)
if err != nil {
return err
}
return json.Unmarshal(b, &c)
}

func (f *Filter) InitFromProtocol(ctx context.Context, c *cptype.Component) error {
b, err := json.Marshal(c)
if err != nil {
return err
}
return json.Unmarshal(b, f)
}
Loading