kanbn2md/main.go
Mike Phares b65befe761
Some checks failed
release-please / release-please (push) Has been cancelled
Created and Relations
2023-06-30 13:14:19 -07:00

137 lines
3.4 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
)
// KanbnColumns are the board columns as represented in the kanbn json
type KanbnColumns [][]struct {
Column string `json:"column"`
Comments []interface{} `json:"comments"`
Description string `json:"description"`
ID string `json:"id"`
Metadata struct {
Assigned string `json:"assigned"`
Created string `json:"created"`
Completed string `json:"completed"`
Progress int `json:"progress"`
Started *string `json:"started,omitempty"`
Tags []interface{} `json:"tags"`
Updated string `json:"updated"`
} `json:"metadata"`
Name string `json:"name"`
Progress int `json:"progress"`
Relations []struct {
Task string `json:"task"`
Type string `json:"type"`
} `json:"relations"`
RemainingWorkload int `json:"remainingWorkload"`
SubTasks []struct {
Completed bool `json:"completed"`
Text string `json:"text"`
} `json:"subTasks"`
Workload int `json:"workload"`
}
// KanbnBoard represents the JSON output of `kanbn board -j`
type KanbnBoard struct {
Headings []struct {
Heading string `json:"heading"`
Name string `json:"name"`
} `json:"headings"`
Lanes []struct {
Columns KanbnColumns `json:"columns"`
Name string `json:"name"`
} `json:"lanes"`
}
func main() {
err := renderBoard(os.Stdin, os.Stdout)
if err != nil {
log.Fatal(err)
}
}
// renderBoard takes care of reading the kanbn json
// and rendering it as a markdown table
func renderBoard(r io.Reader, w io.Writer) error {
var kanbn KanbnBoard
var rows [][]string
board, err := ioutil.ReadAll(r)
if err != nil {
return err
}
if err = json.Unmarshal(board, &kanbn); err != nil {
return err
}
fmt.Fprintln(w, "<style>")
fmt.Fprintln(w, "created { color: gray }")
fmt.Fprintln(w, "relations { color: Green }")
fmt.Fprintln(w, "</style>")
// Determine how many columns exists and print the header row
columnCount := len(kanbn.Headings)
for _, col := range kanbn.Headings {
fmt.Fprintf(w, "| %s ", col.Name)
}
fmt.Fprintln(w, "|")
for i := 0; i < columnCount; i++ {
fmt.Fprint(w, "| --- ")
}
fmt.Fprintln(w, "|")
// read the swimlanes
// The lanes themselves are not printed on the table
for _, lane := range kanbn.Lanes {
maxRows := determineMaxRows(lane.Columns)
// initialize an array of rows
rows = make([][]string, maxRows)
for i := range rows {
rows[i] = make([]string, columnCount)
}
// convert columns of rows into rows of columns
for curCol, column := range lane.Columns {
for colRow, task := range column {
complete := ""
firstRelation := ""
if task.Progress == 1 || task.Metadata.Completed != "" {
complete = "~~"
}
if len(task.Relations) > 0 {
firstRelation = task.Relations[0].Task
}
rows[colRow][curCol] = fmt.Sprintf("%s%s%s<br />%s<br />%s",
complete,
task.Name,
complete,
"<created>"+task.Metadata.Created[0:10]+"</created>",
"<relations>"+firstRelation+"</relations>")
}
}
// print the rows
for _, row := range rows {
for _, col := range row {
fmt.Fprintf(w, "| %s ", col)
}
fmt.Fprintln(w, "|")
}
}
return nil
}
func determineMaxRows(cols KanbnColumns) int {
var maxRows int
for _, column := range cols {
if len(column) > maxRows {
maxRows = len(column)
}
}
return maxRows
}