From 1220b05eb67466068afdf85b6356090eb491684b Mon Sep 17 00:00:00 2001 From: ddddddO Date: Sat, 5 Feb 2022 00:43:15 +0900 Subject: [PATCH 1/5] rm gantt --- README.md | 58 ------------------------ gantt.go | 109 ---------------------------------------------- gantt.html | 27 ------------ gantt.png | Bin 77735 -> 0 bytes gantt_template.go | 18 -------- gdag.go | 15 ------- gdag_test.go | 71 ------------------------------ 7 files changed, 298 deletions(-) delete mode 100644 gantt.go delete mode 100644 gantt.html delete mode 100644 gantt.png delete mode 100644 gantt_template.go diff --git a/README.md b/README.md index 98cf707..a71bfd3 100644 --- a/README.md +++ b/README.md @@ -166,64 +166,6 @@ func main() { - [ ] リリース - [x] finish -## [WIP] GanttChart -1. `go run main.go > gantt.html` - -```go -package main - -import ( - g "github.com/ddddddO/gdag" -) - -func main() { - var goal *g.Node = g.Goal("ゴール(目的)") - - var design *g.Node = g.Task("設計") - design.WithGanttStart("2021-9-3", 1) - reviewDesign := g.Task("レビュー対応") - reviewDesign.WithGantt(1) - - developFeature1 := g.Task("feature1開発") - developFeature1.Note("xxが担当") - developFeature1.WithGantt(1) - reviewDevelopFeature1 := g.Task("レビュー対応") - reviewDevelopFeature1.WithGantt(1) - - developFeature2 := g.Task("feature2開発") - developFeature2.Note("yyが担当") - developFeature2.WithGantt(4) - reviewDevelopFeature2 := g.Task("レビュー対応") - reviewDevelopFeature2.WithGantt(1) - - prepareInfra := g.Task("インフラ準備") - prepareInfra.Note("zzが担当") - prepareInfra.WithGantt(2) - - test := g.Task("結合テスト") - test.WithGantt(1) - release := g.Task("リリース") - release.WithGantt(1) - finish := g.Task("finish") - finish.WithGantt(1) - - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) - reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) - reviewDesign.Con(prepareInfra).Con(test) - test.Con(release).Con(finish) - - g.Done(design, reviewDesign, developFeature2, finish) - - if err := g.GenerateGantt(goal); err != nil { - panic(err) - } -} -``` - -2. open gantt.html - -![image](https://github.com/ddddddO/gdag/blob/main/gantt.png) - ## etc ### short name version diff --git a/gantt.go b/gantt.go deleted file mode 100644 index 5a0b3fa..0000000 --- a/gantt.go +++ /dev/null @@ -1,109 +0,0 @@ -package gdag - -import ( - "fmt" - "sort" -) - -type gantt struct { - title string // text of Node - identifier int // as of Node - action string // e.g. active/done ... - - // startDateに空文字以外の日付が入っていれば、startDate + , + term + d - // startDateが空文字であれば、 after + 上流タスクのidentifier + , + term + d - startDate string // e.g. 2014-01-07 - term int // e.g. 3(days) -> after upstream, 3d - criticalpath path -} - -type path struct { - nodes []*Node - terms int -} - -func newGantt(title string, identifier int, startDate string, term int) gantt { - return gantt{ - title: title, - identifier: identifier, - startDate: startDate, - term: term, - } -} - -// WithGanttStart is ゴール(rectangle)直下のタスク(usecase)に一回呼び出す。 -// その後ろのタスクはWithGantt funcを呼び出す。なんか微妙な -func (n *Node) WithGanttStart(startDate string, term int) { - n.gantt = newGantt(n.text, n.as, startDate, term) -} - -func (n *Node) WithGantt(term int) { - n.gantt = newGantt(n.text, n.as, "", term) -} - -func GenerateGantt(node *Node) error { - gantt := generateGantt(node) - out := fmt.Sprintf(ganttTemplate, gantt) - fmt.Println(out) - return nil -} - -func generateGantt(node *Node) string { - uniqGantt(node) - sorted := sortGantt(uniqG) - return generateG(sorted) -} - -var uniqG = make(map[int]*Node) - -func uniqGantt(n *Node) { - if _, ok := uniqG[n.as]; ok { - return - } - uniqG[n.as] = n - - for _, d := range n.downstream { - uniqGantt(d) - } -} - -func sortGantt(uniq map[int]*Node) []*Node { - keys := make([]int, 0, len(uniq)) - for k := range uniq { - keys = append(keys, k) - } - sort.Ints(keys) - - sorted := make([]*Node, 0, len(keys)) - for _, k := range keys { - v := uniq[k] - sorted = append(sorted, v) - } - - return sorted -} - -func generateG(sorted []*Node) string { - out := "" - for _, n := range sorted { - tmp := "" - if n.nodeType == rectangle { - tmp += "section " + n.text - } - if n.nodeType == usecase { - tmp += "\t" + n.gantt.title + " :" + fmt.Sprintf("%d", n.gantt.identifier) + "," - // TODO: このあたりにactionいれる - if len(n.gantt.startDate) != 0 { - tmp += n.gantt.startDate + "," - } else { - preNodeIdx := len(n.gantt.criticalpath.nodes) - 2 - after := n.gantt.criticalpath.nodes[preNodeIdx].gantt - tmp += fmt.Sprintf("after %d", after.identifier) + "," - } - tmp += fmt.Sprintf("%dd", n.gantt.term) - } - tmp += "\n" - out += tmp - } - return out -} diff --git a/gantt.html b/gantt.html deleted file mode 100644 index 0222626..0000000 --- a/gantt.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - Here is one mermaid diagram: -
- gantt - dateFormat YYYY-MM-DD - - section ゴール(目的) - 設計 :2,2021-9-3,1d - レビュー対応 :3,after 2,1d - feature1開発 :4,after 3,1d - レビュー対応 :5,after 4,1d - feature2開発 :6,after 3,4d - レビュー対応 :7,after 6,1d - インフラ準備 :8,after 3,2d - 結合テスト :9,after 7,1d - リリース :10,after 9,1d - finish :11,after 10,1d - -
- - diff --git a/gantt.png b/gantt.png deleted file mode 100644 index 480971cdb3e91a46890e7639020541ae5412e712..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77735 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

1+zb8$@{EHapnL}H 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 A9smFU diff --git a/gantt_template.go b/gantt_template.go deleted file mode 100644 index ce371d3..0000000 --- a/gantt_template.go +++ /dev/null @@ -1,18 +0,0 @@ -package gdag - -var ganttTemplate = ` - - - - - Here is one mermaid diagram: -

- gantt - dateFormat YYYY-MM-DD - - %s -
- -` diff --git a/gdag.go b/gdag.go index f625804..c4d565c 100644 --- a/gdag.go +++ b/gdag.go @@ -23,8 +23,6 @@ type Node struct { note string color string // done: #DarkGray - gantt gantt - upstream []*Node downstream []*Node } @@ -69,19 +67,6 @@ func (upstream *Node) Con(current *Node) *Node { upstream.downstream = append(upstream.downstream, current) current.upstream = append(current.upstream, upstream) - // TODO: - // ここにクリティカルパス用の計算をいれるのは微妙な気がする - tmpTerms := 0 - upstreamCritpath := path{} - for _, up := range current.upstream { - if up.gantt.criticalpath.terms > tmpTerms { - tmpTerms = up.gantt.criticalpath.terms - upstreamCritpath = up.gantt.criticalpath - } - } - current.gantt.criticalpath = upstreamCritpath - current.gantt.criticalpath.nodes = append(current.gantt.criticalpath.nodes, current) - current.gantt.criticalpath.terms += current.gantt.term return current } diff --git a/gdag_test.go b/gdag_test.go index 03c32b7..09b7ee6 100644 --- a/gdag_test.go +++ b/gdag_test.go @@ -257,74 +257,3 @@ func ExampleGenerateCheckList() { // - [ ] リリース // - [x] finish } - -func ExampleGenerateGantt() { - var goal *g.Node = g.Goal("ゴール(目的)") - - var design *g.Node = g.Task("設計") - design.WithGanttStart("2021-9-3", 1) - reviewDesign := g.Task("レビュー対応") - reviewDesign.WithGantt(1) - - developFeature1 := g.Task("feature1開発") - developFeature1.Note("xxが担当") - developFeature1.WithGantt(1) - reviewDevelopFeature1 := g.Task("レビュー対応") - reviewDevelopFeature1.WithGantt(1) - - developFeature2 := g.Task("feature2開発") - developFeature2.Note("yyが担当") - developFeature2.WithGantt(4) - reviewDevelopFeature2 := g.Task("レビュー対応") - reviewDevelopFeature2.WithGantt(1) - - prepareInfra := g.Task("インフラ準備") - prepareInfra.Note("zzが担当") - prepareInfra.WithGantt(2) - - test := g.Task("結合テスト") - test.WithGantt(1) - release := g.Task("リリース") - release.WithGantt(1) - finish := g.Task("finish") - finish.WithGantt(1) - - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) - reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) - reviewDesign.Con(prepareInfra).Con(test) - test.Con(release).Con(finish) - - g.Done(design, reviewDesign, developFeature2, finish) - - if err := g.GenerateGantt(goal); err != nil { - panic(err) - } - // Output: - // - // - // - // - // - // Here is one mermaid diagram: - //
- // gantt - // dateFormat YYYY-MM-DD - // - // section ゴール(目的) - // 設計 :46,2021-9-3,1d - // レビュー対応 :47,after 46,1d - // feature1開発 :48,after 47,1d - // レビュー対応 :49,after 48,1d - // feature2開発 :50,after 47,4d - // レビュー対応 :51,after 50,1d - // インフラ準備 :52,after 47,2d - // 結合テスト :53,after 51,1d - // リリース :54,after 53,1d - // finish :55,after 54,1d - // - //
- // - // -} From cd18a596f8dfc2a3e343bbfb2ae99a50c324ba5e Mon Sep 17 00:00:00 2001 From: ddddddO Date: Sat, 5 Feb 2022 00:43:52 +0900 Subject: [PATCH 2/5] go fmt --- gdag.go | 382 ++++++++++++++++++------------------- gdag_short.go | 2 +- gdag_test.go | 518 +++++++++++++++++++++++++------------------------- 3 files changed, 451 insertions(+), 451 deletions(-) diff --git a/gdag.go b/gdag.go index c4d565c..e09ab54 100644 --- a/gdag.go +++ b/gdag.go @@ -1,191 +1,191 @@ -package gdag - -import ( - "fmt" - "sort" -) - -type nodeType string - -const ( - rectangle = nodeType("rectangle") - usecase = nodeType("usecase") -) - -type Node struct { - nodeType nodeType - text string - // plantumlでの識別子。自動で生成したいけど、例えばa, b, ...とかにしちゃうと、他の人がplantumlを編集するとき辛くなる。あと、識別子なので重複する場合はエラーとする。 - // の予定だったが、自動で生成する。連番。たぶん他の人がumlいじることはないとも思う。 - // せめて、連番ではなく、置換しやすいように少し長めのユニークなIDにしたほうがいいかも。テストできそうかも考えて実装した方が良さそう。 - // かつ、ソータブルな値が必須(ソートして使っているところがあるため) - as int - note string - color string // done: #DarkGray - - upstream []*Node - downstream []*Node -} - -var nodeIdx int - -func newNode(nodeType nodeType, text string) *Node { - nodeIdx++ - - return &Node{ - nodeType: nodeType, - text: text, - as: nodeIdx, - } -} - -func Goal(text string) *Node { - return newNode(rectangle, text) -} - -func Task(text string) *Node { - return newNode(usecase, text) -} - -const ( - colorDone = "#DarkGray" -) - -func Done(nodes ...*Node) { - for _, n := range nodes { - n.color = colorDone - } -} - -func (upstream *Node) Con(current *Node) *Node { - for _, d := range upstream.downstream { - if current.as == d.as { - return d - } - } - - upstream.downstream = append(upstream.downstream, current) - current.upstream = append(current.upstream, upstream) - - return current -} - -func (current *Node) Note(note string) { - current.note = note -} - -func (current *Node) isDone() bool { - return current.color == colorDone -} - -func GenerateUML(node *Node) error { - fmt.Println("@startuml") - - fmt.Println(generateComponents(node)) - fmt.Println(generateRelations(node)) - - fmt.Println("@enduml") - return nil -} - -func generateComponents(node *Node) string { - return generateComponent(node) -} - -var uniqC = make(map[int]struct{}) - -func generateComponent(n *Node) string { - if _, ok := uniqC[n.as]; ok { - return "" - } - uniqC[n.as] = struct{}{} - - dst := "" - switch n.nodeType { - case rectangle, usecase: - s := fmt.Sprintf("%s \"%s\" as %d", n.nodeType, n.text, n.as) - if len(n.color) != 0 { - s += fmt.Sprintf(" %s", n.color) - } - s += "\n" - - dst += s - } - if len(n.note) != 0 { - dst += fmt.Sprintf("note left\n%s\nend note\n", n.note) - } - - for _, d := range n.downstream { - dst += generateComponent(d) - } - - return dst -} - -func generateRelations(node *Node) string { - return generateRelation(node, "") -} - -var uniqR = make(map[string]struct{}) - -func generateRelation(n *Node, out string) string { - r := fmt.Sprintf("%d --> ", n.as) - for _, d := range n.downstream { - key := fmt.Sprintf("%d-%d", n.as, d.as) - if _, ok := uniqR[key]; ok { - continue - } - uniqR[key] = struct{}{} - - tmp := fmt.Sprintf("%s%d\n", r, d.as) - out += generateRelation(d, tmp) - } - return out -} - -// 要改修。本当はリレーションに基づいて生成した方が良さそうに思う。 -func GenerateCheckList(n *Node) error { - uniqAs(n) - sorted := sortComponentList(uniqAS) - - out := "" - for _, node := range sorted { - if node.isDone() { - out += fmt.Sprintf("- [x] %s\n", node.text) - } else { - out += fmt.Sprintf("- [ ] %s\n", node.text) - } - } - fmt.Print(out) - - return nil -} - -var uniqAS = make(map[int]*Node) - -func uniqAs(n *Node) { - if _, ok := uniqAS[n.as]; ok { - return - } - uniqAS[n.as] = n - - for _, d := range n.downstream { - uniqAs(d) - } -} - -func sortComponentList(uniq map[int]*Node) []*Node { - keys := make([]int, 0, len(uniq)) - for k := range uniq { - keys = append(keys, k) - } - sort.Ints(keys) - - sorted := make([]*Node, 0, len(keys)) - for _, k := range keys { - v := uniq[k] - sorted = append(sorted, v) - } - - return sorted -} +package gdag + +import ( + "fmt" + "sort" +) + +type nodeType string + +const ( + rectangle = nodeType("rectangle") + usecase = nodeType("usecase") +) + +type Node struct { + nodeType nodeType + text string + // plantumlでの識別子。自動で生成したいけど、例えばa, b, ...とかにしちゃうと、他の人がplantumlを編集するとき辛くなる。あと、識別子なので重複する場合はエラーとする。 + // の予定だったが、自動で生成する。連番。たぶん他の人がumlいじることはないとも思う。 + // せめて、連番ではなく、置換しやすいように少し長めのユニークなIDにしたほうがいいかも。テストできそうかも考えて実装した方が良さそう。 + // かつ、ソータブルな値が必須(ソートして使っているところがあるため) + as int + note string + color string // done: #DarkGray + + upstream []*Node + downstream []*Node +} + +var nodeIdx int + +func newNode(nodeType nodeType, text string) *Node { + nodeIdx++ + + return &Node{ + nodeType: nodeType, + text: text, + as: nodeIdx, + } +} + +func Goal(text string) *Node { + return newNode(rectangle, text) +} + +func Task(text string) *Node { + return newNode(usecase, text) +} + +const ( + colorDone = "#DarkGray" +) + +func Done(nodes ...*Node) { + for _, n := range nodes { + n.color = colorDone + } +} + +func (upstream *Node) Con(current *Node) *Node { + for _, d := range upstream.downstream { + if current.as == d.as { + return d + } + } + + upstream.downstream = append(upstream.downstream, current) + current.upstream = append(current.upstream, upstream) + + return current +} + +func (current *Node) Note(note string) { + current.note = note +} + +func (current *Node) isDone() bool { + return current.color == colorDone +} + +func GenerateUML(node *Node) error { + fmt.Println("@startuml") + + fmt.Println(generateComponents(node)) + fmt.Println(generateRelations(node)) + + fmt.Println("@enduml") + return nil +} + +func generateComponents(node *Node) string { + return generateComponent(node) +} + +var uniqC = make(map[int]struct{}) + +func generateComponent(n *Node) string { + if _, ok := uniqC[n.as]; ok { + return "" + } + uniqC[n.as] = struct{}{} + + dst := "" + switch n.nodeType { + case rectangle, usecase: + s := fmt.Sprintf("%s \"%s\" as %d", n.nodeType, n.text, n.as) + if len(n.color) != 0 { + s += fmt.Sprintf(" %s", n.color) + } + s += "\n" + + dst += s + } + if len(n.note) != 0 { + dst += fmt.Sprintf("note left\n%s\nend note\n", n.note) + } + + for _, d := range n.downstream { + dst += generateComponent(d) + } + + return dst +} + +func generateRelations(node *Node) string { + return generateRelation(node, "") +} + +var uniqR = make(map[string]struct{}) + +func generateRelation(n *Node, out string) string { + r := fmt.Sprintf("%d --> ", n.as) + for _, d := range n.downstream { + key := fmt.Sprintf("%d-%d", n.as, d.as) + if _, ok := uniqR[key]; ok { + continue + } + uniqR[key] = struct{}{} + + tmp := fmt.Sprintf("%s%d\n", r, d.as) + out += generateRelation(d, tmp) + } + return out +} + +// 要改修。本当はリレーションに基づいて生成した方が良さそうに思う。 +func GenerateCheckList(n *Node) error { + uniqAs(n) + sorted := sortComponentList(uniqAS) + + out := "" + for _, node := range sorted { + if node.isDone() { + out += fmt.Sprintf("- [x] %s\n", node.text) + } else { + out += fmt.Sprintf("- [ ] %s\n", node.text) + } + } + fmt.Print(out) + + return nil +} + +var uniqAS = make(map[int]*Node) + +func uniqAs(n *Node) { + if _, ok := uniqAS[n.as]; ok { + return + } + uniqAS[n.as] = n + + for _, d := range n.downstream { + uniqAs(d) + } +} + +func sortComponentList(uniq map[int]*Node) []*Node { + keys := make([]int, 0, len(uniq)) + for k := range uniq { + keys = append(keys, k) + } + sort.Ints(keys) + + sorted := make([]*Node, 0, len(keys)) + for _, k := range keys { + v := uniq[k] + sorted = append(sorted, v) + } + + return sorted +} diff --git a/gdag_short.go b/gdag_short.go index a103403..e59133c 100644 --- a/gdag_short.go +++ b/gdag_short.go @@ -33,4 +33,4 @@ func GUML(node *Node) error { // GCL is short name of GenerateCheckList func. func GCL(node *Node) error { return GenerateCheckList(node) -} \ No newline at end of file +} diff --git a/gdag_test.go b/gdag_test.go index 09b7ee6..996c1cf 100644 --- a/gdag_test.go +++ b/gdag_test.go @@ -1,259 +1,259 @@ -package gdag_test - -import ( - g "github.com/ddddddO/gdag" -) - -func Example() { - var goal *g.Node = g.Goal("ゴール(目的)") - - var design *g.Node = g.Task("設計") - reviewDesign := g.Task("レビュー対応") - - developFeature1 := g.Task("feature1開発") - developFeature1.Note("xxが担当") - reviewDevelopFeature1 := g.Task("レビュー対応") - - developFeature2 := g.Task("feature2開発") - developFeature2.Note("yyが担当") - reviewDevelopFeature2 := g.Task("レビュー対応") - - prepareInfra := g.Task("インフラ準備") - prepareInfra.Note("zzが担当") - - test := g.Task("結合テスト") - release := g.Task("リリース") - finish := g.Task("finish") - - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) - reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) - reviewDesign.Con(prepareInfra).Con(test) - test.Con(release).Con(finish) - - g.Done(design, reviewDesign, developFeature2, finish) - - if err := g.GenerateUML(goal); err != nil { - panic(err) - } - // Output: - // @startuml - // rectangle "ゴール(目的)" as 1 - // usecase "設計" as 2 #DarkGray - // usecase "レビュー対応" as 3 #DarkGray - // usecase "feature1開発" as 4 - // note left - // xxが担当 - // end note - // usecase "レビュー対応" as 5 - // usecase "結合テスト" as 9 - // usecase "リリース" as 10 - // usecase "finish" as 11 #DarkGray - // usecase "feature2開発" as 6 #DarkGray - // note left - // yyが担当 - // end note - // usecase "レビュー対応" as 7 - // usecase "インフラ準備" as 8 - // note left - // zzが担当 - // end note - // - // 1 --> 2 - // 2 --> 3 - // 3 --> 4 - // 4 --> 5 - // 5 --> 9 - // 9 --> 10 - // 10 --> 11 - // 3 --> 6 - // 6 --> 7 - // 7 --> 9 - // 3 --> 8 - // 8 --> 9 - // - // @enduml -} - -func ExampleGenerateUML() { - var goal *g.Node = g.Goal("ゴール(目的)") - - var design *g.Node = g.Task("設計") - reviewDesign := g.Task("レビュー対応") - - developFeature1 := g.Task("feature1開発") - developFeature1.Note("xxが担当") - reviewDevelopFeature1 := g.Task("レビュー対応") - - developFeature2 := g.Task("feature2開発") - developFeature2.Note("yyが担当") - reviewDevelopFeature2 := g.Task("レビュー対応") - - prepareInfra := g.Task("インフラ準備") - prepareInfra.Note("zzが担当") - - test := g.Task("結合テスト") - release := g.Task("リリース") - finish := g.Task("finish") - - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) - reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) - reviewDesign.Con(prepareInfra).Con(test) - test.Con(release).Con(finish) - - g.Done(design, reviewDesign, developFeature2, finish) - - if err := g.GenerateUML(goal); err != nil { - panic(err) - } - // Output: - // @startuml - // rectangle "ゴール(目的)" as 12 - // usecase "設計" as 13 #DarkGray - // usecase "レビュー対応" as 14 #DarkGray - // usecase "feature1開発" as 15 - // note left - // xxが担当 - // end note - // usecase "レビュー対応" as 16 - // usecase "結合テスト" as 20 - // usecase "リリース" as 21 - // usecase "finish" as 22 #DarkGray - // usecase "feature2開発" as 17 #DarkGray - // note left - // yyが担当 - // end note - // usecase "レビュー対応" as 18 - // usecase "インフラ準備" as 19 - // note left - // zzが担当 - // end note - // - // 12 --> 13 - // 13 --> 14 - // 14 --> 15 - // 15 --> 16 - // 16 --> 20 - // 20 --> 21 - // 21 --> 22 - // 14 --> 17 - // 17 --> 18 - // 18 --> 20 - // 14 --> 19 - // 19 --> 20 - // - // @enduml -} - -func ExampleGUML() { - var goal *g.Node = g.G("ゴール(目的)") - - var design *g.Node = g.T("設計") - reviewDesign := g.T("レビュー対応") - - developFeature1 := g.T("feature1開発") - developFeature1.N("xxが担当") - reviewDevelopFeature1 := g.T("レビュー対応") - - developFeature2 := g.T("feature2開発") - developFeature2.N("yyが担当") - reviewDevelopFeature2 := g.T("レビュー対応") - - prepareInfra := g.T("インフラ準備") - prepareInfra.N("zzが担当") - - test := g.T("結合テスト") - release := g.T("リリース") - finish := g.T("finish") - - goal.C(design).C(reviewDesign).C(developFeature1).C(reviewDevelopFeature1).C(test) - reviewDesign.C(developFeature2).C(reviewDevelopFeature2).C(test) - reviewDesign.C(prepareInfra).C(test) - test.C(release).C(finish) - - g.D(design, reviewDesign, developFeature2, finish) - - if err := g.GUML(goal); err != nil { - panic(err) - } - // Output: - // @startuml - // rectangle "ゴール(目的)" as 23 - // usecase "設計" as 24 #DarkGray - // usecase "レビュー対応" as 25 #DarkGray - // usecase "feature1開発" as 26 - // note left - // xxが担当 - // end note - // usecase "レビュー対応" as 27 - // usecase "結合テスト" as 31 - // usecase "リリース" as 32 - // usecase "finish" as 33 #DarkGray - // usecase "feature2開発" as 28 #DarkGray - // note left - // yyが担当 - // end note - // usecase "レビュー対応" as 29 - // usecase "インフラ準備" as 30 - // note left - // zzが担当 - // end note - // - // 23 --> 24 - // 24 --> 25 - // 25 --> 26 - // 26 --> 27 - // 27 --> 31 - // 31 --> 32 - // 32 --> 33 - // 25 --> 28 - // 28 --> 29 - // 29 --> 31 - // 25 --> 30 - // 30 --> 31 - // - // @enduml -} - -func ExampleGenerateCheckList() { - goal := g.Goal("ゴール(目的)") - - design := g.Task("設計") - reviewDesign := g.Task("レビュー対応") - - developFeature1 := g.Task("feature1開発") - developFeature1.Note("xxが担当") - reviewDevelopFeature1 := g.Task("レビュー対応") - - developFeature2 := g.Task("feature2開発") - developFeature2.Note("yyが担当") - reviewDevelopFeature2 := g.Task("レビュー対応") - - prepareInfra := g.Task("インフラ準備") - prepareInfra.Note("zzが担当") - - test := g.Task("結合テスト") - release := g.Task("リリース") - finish := g.Task("finish") - - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) - reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) - reviewDesign.Con(prepareInfra).Con(test) - test.Con(release).Con(finish) - - g.Done(design, reviewDesign, developFeature2, finish) - - if err := g.GenerateCheckList(design); err != nil { - panic(err) - } - // Output: - // - [x] 設計 - // - [x] レビュー対応 - // - [ ] feature1開発 - // - [ ] レビュー対応 - // - [x] feature2開発 - // - [ ] レビュー対応 - // - [ ] インフラ準備 - // - [ ] 結合テスト - // - [ ] リリース - // - [x] finish -} +package gdag_test + +import ( + g "github.com/ddddddO/gdag" +) + +func Example() { + var goal *g.Node = g.Goal("ゴール(目的)") + + var design *g.Node = g.Task("設計") + reviewDesign := g.Task("レビュー対応") + + developFeature1 := g.Task("feature1開発") + developFeature1.Note("xxが担当") + reviewDevelopFeature1 := g.Task("レビュー対応") + + developFeature2 := g.Task("feature2開発") + developFeature2.Note("yyが担当") + reviewDevelopFeature2 := g.Task("レビュー対応") + + prepareInfra := g.Task("インフラ準備") + prepareInfra.Note("zzが担当") + + test := g.Task("結合テスト") + release := g.Task("リリース") + finish := g.Task("finish") + + goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) + reviewDesign.Con(prepareInfra).Con(test) + test.Con(release).Con(finish) + + g.Done(design, reviewDesign, developFeature2, finish) + + if err := g.GenerateUML(goal); err != nil { + panic(err) + } + // Output: + // @startuml + // rectangle "ゴール(目的)" as 1 + // usecase "設計" as 2 #DarkGray + // usecase "レビュー対応" as 3 #DarkGray + // usecase "feature1開発" as 4 + // note left + // xxが担当 + // end note + // usecase "レビュー対応" as 5 + // usecase "結合テスト" as 9 + // usecase "リリース" as 10 + // usecase "finish" as 11 #DarkGray + // usecase "feature2開発" as 6 #DarkGray + // note left + // yyが担当 + // end note + // usecase "レビュー対応" as 7 + // usecase "インフラ準備" as 8 + // note left + // zzが担当 + // end note + // + // 1 --> 2 + // 2 --> 3 + // 3 --> 4 + // 4 --> 5 + // 5 --> 9 + // 9 --> 10 + // 10 --> 11 + // 3 --> 6 + // 6 --> 7 + // 7 --> 9 + // 3 --> 8 + // 8 --> 9 + // + // @enduml +} + +func ExampleGenerateUML() { + var goal *g.Node = g.Goal("ゴール(目的)") + + var design *g.Node = g.Task("設計") + reviewDesign := g.Task("レビュー対応") + + developFeature1 := g.Task("feature1開発") + developFeature1.Note("xxが担当") + reviewDevelopFeature1 := g.Task("レビュー対応") + + developFeature2 := g.Task("feature2開発") + developFeature2.Note("yyが担当") + reviewDevelopFeature2 := g.Task("レビュー対応") + + prepareInfra := g.Task("インフラ準備") + prepareInfra.Note("zzが担当") + + test := g.Task("結合テスト") + release := g.Task("リリース") + finish := g.Task("finish") + + goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) + reviewDesign.Con(prepareInfra).Con(test) + test.Con(release).Con(finish) + + g.Done(design, reviewDesign, developFeature2, finish) + + if err := g.GenerateUML(goal); err != nil { + panic(err) + } + // Output: + // @startuml + // rectangle "ゴール(目的)" as 12 + // usecase "設計" as 13 #DarkGray + // usecase "レビュー対応" as 14 #DarkGray + // usecase "feature1開発" as 15 + // note left + // xxが担当 + // end note + // usecase "レビュー対応" as 16 + // usecase "結合テスト" as 20 + // usecase "リリース" as 21 + // usecase "finish" as 22 #DarkGray + // usecase "feature2開発" as 17 #DarkGray + // note left + // yyが担当 + // end note + // usecase "レビュー対応" as 18 + // usecase "インフラ準備" as 19 + // note left + // zzが担当 + // end note + // + // 12 --> 13 + // 13 --> 14 + // 14 --> 15 + // 15 --> 16 + // 16 --> 20 + // 20 --> 21 + // 21 --> 22 + // 14 --> 17 + // 17 --> 18 + // 18 --> 20 + // 14 --> 19 + // 19 --> 20 + // + // @enduml +} + +func ExampleGUML() { + var goal *g.Node = g.G("ゴール(目的)") + + var design *g.Node = g.T("設計") + reviewDesign := g.T("レビュー対応") + + developFeature1 := g.T("feature1開発") + developFeature1.N("xxが担当") + reviewDevelopFeature1 := g.T("レビュー対応") + + developFeature2 := g.T("feature2開発") + developFeature2.N("yyが担当") + reviewDevelopFeature2 := g.T("レビュー対応") + + prepareInfra := g.T("インフラ準備") + prepareInfra.N("zzが担当") + + test := g.T("結合テスト") + release := g.T("リリース") + finish := g.T("finish") + + goal.C(design).C(reviewDesign).C(developFeature1).C(reviewDevelopFeature1).C(test) + reviewDesign.C(developFeature2).C(reviewDevelopFeature2).C(test) + reviewDesign.C(prepareInfra).C(test) + test.C(release).C(finish) + + g.D(design, reviewDesign, developFeature2, finish) + + if err := g.GUML(goal); err != nil { + panic(err) + } + // Output: + // @startuml + // rectangle "ゴール(目的)" as 23 + // usecase "設計" as 24 #DarkGray + // usecase "レビュー対応" as 25 #DarkGray + // usecase "feature1開発" as 26 + // note left + // xxが担当 + // end note + // usecase "レビュー対応" as 27 + // usecase "結合テスト" as 31 + // usecase "リリース" as 32 + // usecase "finish" as 33 #DarkGray + // usecase "feature2開発" as 28 #DarkGray + // note left + // yyが担当 + // end note + // usecase "レビュー対応" as 29 + // usecase "インフラ準備" as 30 + // note left + // zzが担当 + // end note + // + // 23 --> 24 + // 24 --> 25 + // 25 --> 26 + // 26 --> 27 + // 27 --> 31 + // 31 --> 32 + // 32 --> 33 + // 25 --> 28 + // 28 --> 29 + // 29 --> 31 + // 25 --> 30 + // 30 --> 31 + // + // @enduml +} + +func ExampleGenerateCheckList() { + goal := g.Goal("ゴール(目的)") + + design := g.Task("設計") + reviewDesign := g.Task("レビュー対応") + + developFeature1 := g.Task("feature1開発") + developFeature1.Note("xxが担当") + reviewDevelopFeature1 := g.Task("レビュー対応") + + developFeature2 := g.Task("feature2開発") + developFeature2.Note("yyが担当") + reviewDevelopFeature2 := g.Task("レビュー対応") + + prepareInfra := g.Task("インフラ準備") + prepareInfra.Note("zzが担当") + + test := g.Task("結合テスト") + release := g.Task("リリース") + finish := g.Task("finish") + + goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) + reviewDesign.Con(prepareInfra).Con(test) + test.Con(release).Con(finish) + + g.Done(design, reviewDesign, developFeature2, finish) + + if err := g.GenerateCheckList(design); err != nil { + panic(err) + } + // Output: + // - [x] 設計 + // - [x] レビュー対応 + // - [ ] feature1開発 + // - [ ] レビュー対応 + // - [x] feature2開発 + // - [ ] レビュー対応 + // - [ ] インフラ準備 + // - [ ] 結合テスト + // - [ ] リリース + // - [x] finish +} From 0f4d7b08a9b06655bfaa71022ba8f2e32603d784 Mon Sep 17 00:00:00 2001 From: ddddddO Date: Sat, 5 Feb 2022 01:06:28 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E9=96=A2=E6=95=B0=E3=81=ABnode=E3=82=92?= =?UTF-8?q?=E6=B8=A1=E3=81=97=E3=81=A6=E5=87=BA=E5=8A=9B=E3=81=A7=E3=81=AF?= =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=80=81=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89?= =?UTF-8?q?=E3=81=A7=E5=87=BA=E5=8A=9B=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gdag.go | 39 ++++++++++++++++++--------------------- gdag_short.go | 10 ---------- gdag_test.go | 20 +++++++++++++++----- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/gdag.go b/gdag.go index e09ab54..e715c82 100644 --- a/gdag.go +++ b/gdag.go @@ -78,18 +78,17 @@ func (current *Node) isDone() bool { return current.color == colorDone } -func GenerateUML(node *Node) error { - fmt.Println("@startuml") - - fmt.Println(generateComponents(node)) - fmt.Println(generateRelations(node)) - - fmt.Println("@enduml") - return nil +// UML outputs dag plant UML. +func (current *Node) UML() (string, error) { + ret := "@startuml" + "\n" + ret += current.generateComponents() + "\n" + ret += current.generateRelations() + "\n" + ret += "@enduml" + return ret, nil } -func generateComponents(node *Node) string { - return generateComponent(node) +func (current *Node) generateComponents() string { + return generateComponent(current) } var uniqC = make(map[int]struct{}) @@ -122,8 +121,8 @@ func generateComponent(n *Node) string { return dst } -func generateRelations(node *Node) string { - return generateRelation(node, "") +func (current *Node) generateRelations() string { + return generateRelation(current, "") } var uniqR = make(map[string]struct{}) @@ -143,22 +142,20 @@ func generateRelation(n *Node, out string) string { return out } -// 要改修。本当はリレーションに基づいて生成した方が良さそうに思う。 -func GenerateCheckList(n *Node) error { - uniqAs(n) +// CheckList outputs task check list. +func (current *Node) CheckList() (string, error) { + uniqAs(current) sorted := sortComponentList(uniqAS) - out := "" + ret := "" for _, node := range sorted { if node.isDone() { - out += fmt.Sprintf("- [x] %s\n", node.text) + ret += fmt.Sprintf("- [x] %s\n", node.text) } else { - out += fmt.Sprintf("- [ ] %s\n", node.text) + ret += fmt.Sprintf("- [ ] %s\n", node.text) } } - fmt.Print(out) - - return nil + return ret, nil } var uniqAS = make(map[int]*Node) diff --git a/gdag_short.go b/gdag_short.go index e59133c..7f5baea 100644 --- a/gdag_short.go +++ b/gdag_short.go @@ -24,13 +24,3 @@ func (upstream *Node) C(current *Node) *Node { func (current *Node) N(note string) { current.Note(note) } - -// GUML is short name of GenerateUML func. -func GUML(node *Node) error { - return GenerateUML(node) -} - -// GCL is short name of GenerateCheckList func. -func GCL(node *Node) error { - return GenerateCheckList(node) -} diff --git a/gdag_test.go b/gdag_test.go index 996c1cf..43b003a 100644 --- a/gdag_test.go +++ b/gdag_test.go @@ -1,6 +1,8 @@ package gdag_test import ( + "fmt" + g "github.com/ddddddO/gdag" ) @@ -32,9 +34,11 @@ func Example() { g.Done(design, reviewDesign, developFeature2, finish) - if err := g.GenerateUML(goal); err != nil { + uml, err := goal.UML() + if err != nil { panic(err) } + fmt.Println(uml) // Output: // @startuml // rectangle "ゴール(目的)" as 1 @@ -102,9 +106,11 @@ func ExampleGenerateUML() { g.Done(design, reviewDesign, developFeature2, finish) - if err := g.GenerateUML(goal); err != nil { + uml, err := goal.UML() + if err != nil { panic(err) } + fmt.Println(uml) // Output: // @startuml // rectangle "ゴール(目的)" as 12 @@ -144,7 +150,7 @@ func ExampleGenerateUML() { // @enduml } -func ExampleGUML() { +func ExampleShortMethod() { var goal *g.Node = g.G("ゴール(目的)") var design *g.Node = g.T("設計") @@ -172,9 +178,11 @@ func ExampleGUML() { g.D(design, reviewDesign, developFeature2, finish) - if err := g.GUML(goal); err != nil { + uml, err := goal.UML() + if err != nil { panic(err) } + fmt.Println(uml) // Output: // @startuml // rectangle "ゴール(目的)" as 23 @@ -242,9 +250,11 @@ func ExampleGenerateCheckList() { g.Done(design, reviewDesign, developFeature2, finish) - if err := g.GenerateCheckList(design); err != nil { + checkList, err := design.CheckList() + if err != nil { panic(err) } + fmt.Println(checkList) // Output: // - [x] 設計 // - [x] レビュー対応 From 66df0b3faf3234fcfabf2f4620a9c38e97c0f8f6 Mon Sep 17 00:00:00 2001 From: ddddddO Date: Sat, 5 Feb 2022 01:15:54 +0900 Subject: [PATCH 4/5] func name: Goal to DAG --- README.md | 30 +++++++++++++++++++++--------- gdag.go | 2 +- gdag_short.go | 5 ----- gdag_test.go | 26 +++++++++++++------------- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index a71bfd3..9e2b270 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,13 @@ Useful for progressing tasks. package main import ( + "fmt" + g "github.com/ddddddO/gdag" ) func main() { - var goal *g.Node = g.Goal("ゴール(目的)") + var dag *g.Node = g.DAG("ゴール(目的)") var design *g.Node = g.Task("設計") reviewDesign := g.Task("レビュー対応") @@ -39,16 +41,18 @@ func main() { release := g.Task("リリース") finish := g.Task("finish") - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) reviewDesign.Con(prepareInfra).Con(test) test.Con(release).Con(finish) g.Done(design, reviewDesign, developFeature2, finish) - if err := g.GenerateUML(goal); err != nil { + uml, err := dag.UML() + if err != nil { panic(err) } + fmt.Println(uml) } ``` @@ -104,11 +108,13 @@ end note package main import ( + "fmt" + g "github.com/ddddddO/gdag" ) func main() { - goal := g.Goal("ゴール(目的)") + dag := g.DAG("ゴール(目的)") design := g.Task("設計") reviewDesign := g.Task("レビュー対応") @@ -128,16 +134,18 @@ func main() { release := g.Task("リリース") finish := g.Task("finish") - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) reviewDesign.Con(prepareInfra).Con(test) test.Con(release).Con(finish) g.Done(design, reviewDesign, developFeature2, finish) - if err := g.GenerateCheckList(design); err != nil { + checkList, err := design.CheckList() + if err != nil { panic(err) } + fmt.Println(checkList) } ``` @@ -173,11 +181,13 @@ func main() { package main import ( + "fmt" + g "github.com/ddddddO/gdag" ) func main() { - var goal *g.Node = g.G("ゴール(目的)") + var dag *g.Node = g.DAG("ゴール(目的)") var design *g.Node = g.T("設計") reviewDesign := g.T("レビュー対応") @@ -197,16 +207,18 @@ func main() { release := g.T("リリース") finish := g.T("finish") - goal.C(design).C(reviewDesign).C(developFeature1).C(reviewDevelopFeature1).C(test) + dag.C(design).C(reviewDesign).C(developFeature1).C(reviewDevelopFeature1).C(test) reviewDesign.C(developFeature2).C(reviewDevelopFeature2).C(test) reviewDesign.C(prepareInfra).C(test) test.C(release).C(finish) g.D(design, reviewDesign, developFeature2, finish) - if err := g.GUML(goal); err != nil { + uml, err := dag.UML() + if err != nil { panic(err) } + fmt.Println(uml) } ``` diff --git a/gdag.go b/gdag.go index e715c82..bae16aa 100644 --- a/gdag.go +++ b/gdag.go @@ -39,7 +39,7 @@ func newNode(nodeType nodeType, text string) *Node { } } -func Goal(text string) *Node { +func DAG(text string) *Node { return newNode(rectangle, text) } diff --git a/gdag_short.go b/gdag_short.go index 7f5baea..d57396c 100644 --- a/gdag_short.go +++ b/gdag_short.go @@ -1,10 +1,5 @@ package gdag -// G is short name of Goal func. -func G(text string) *Node { - return Goal(text) -} - // T is short name of Task func. func T(text string) *Node { return Task(text) diff --git a/gdag_test.go b/gdag_test.go index 43b003a..ff6434d 100644 --- a/gdag_test.go +++ b/gdag_test.go @@ -7,7 +7,7 @@ import ( ) func Example() { - var goal *g.Node = g.Goal("ゴール(目的)") + var dag *g.Node = g.DAG("ゴール(目的)") var design *g.Node = g.Task("設計") reviewDesign := g.Task("レビュー対応") @@ -27,14 +27,14 @@ func Example() { release := g.Task("リリース") finish := g.Task("finish") - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) reviewDesign.Con(prepareInfra).Con(test) test.Con(release).Con(finish) g.Done(design, reviewDesign, developFeature2, finish) - uml, err := goal.UML() + uml, err := dag.UML() if err != nil { panic(err) } @@ -78,8 +78,8 @@ func Example() { // @enduml } -func ExampleGenerateUML() { - var goal *g.Node = g.Goal("ゴール(目的)") +func ExampleUML() { + var dag *g.Node = g.DAG("ゴール(目的)") var design *g.Node = g.Task("設計") reviewDesign := g.Task("レビュー対応") @@ -99,14 +99,14 @@ func ExampleGenerateUML() { release := g.Task("リリース") finish := g.Task("finish") - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) reviewDesign.Con(prepareInfra).Con(test) test.Con(release).Con(finish) g.Done(design, reviewDesign, developFeature2, finish) - uml, err := goal.UML() + uml, err := dag.UML() if err != nil { panic(err) } @@ -151,7 +151,7 @@ func ExampleGenerateUML() { } func ExampleShortMethod() { - var goal *g.Node = g.G("ゴール(目的)") + var dag *g.Node = g.DAG("ゴール(目的)") var design *g.Node = g.T("設計") reviewDesign := g.T("レビュー対応") @@ -171,14 +171,14 @@ func ExampleShortMethod() { release := g.T("リリース") finish := g.T("finish") - goal.C(design).C(reviewDesign).C(developFeature1).C(reviewDevelopFeature1).C(test) + dag.C(design).C(reviewDesign).C(developFeature1).C(reviewDevelopFeature1).C(test) reviewDesign.C(developFeature2).C(reviewDevelopFeature2).C(test) reviewDesign.C(prepareInfra).C(test) test.C(release).C(finish) g.D(design, reviewDesign, developFeature2, finish) - uml, err := goal.UML() + uml, err := dag.UML() if err != nil { panic(err) } @@ -222,8 +222,8 @@ func ExampleShortMethod() { // @enduml } -func ExampleGenerateCheckList() { - goal := g.Goal("ゴール(目的)") +func ExampleCheckList() { + dag := g.DAG("ゴール(目的)") design := g.Task("設計") reviewDesign := g.Task("レビュー対応") @@ -243,7 +243,7 @@ func ExampleGenerateCheckList() { release := g.Task("リリース") finish := g.Task("finish") - goal.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) + dag.Con(design).Con(reviewDesign).Con(developFeature1).Con(reviewDevelopFeature1).Con(test) reviewDesign.Con(developFeature2).Con(reviewDevelopFeature2).Con(test) reviewDesign.Con(prepareInfra).Con(test) test.Con(release).Con(finish) From 7e92609e5097eedc51b8fc64aed46a33d31c019c Mon Sep 17 00:00:00 2001 From: ddddddO Date: Sat, 5 Feb 2022 01:18:24 +0900 Subject: [PATCH 5/5] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9e2b270..e52df02 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ DAG is an acronym for Directed Acyclic Graph.
Output is in PlantUML format.
Useful for progressing tasks. +⚠It is incompatible with v0.2.0 and earlier versions⚠ + [![Go Reference](https://pkg.go.dev/badge/github.com/ddddddO/gdag.svg)](https://pkg.go.dev/github.com/ddddddO/gdag) [![GitHub release](https://img.shields.io/github/release/ddddddO/gdag.svg)](https://github.com/ddddddO/gdag/releases) # Demo