mirror of https://github.com/istio/tools.git
222 lines
5.2 KiB
Go
222 lines
5.2 KiB
Go
// Copyright 2021 Istio Authors
|
|
//
|
|
// 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 main
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
var maxScore = 15
|
|
|
|
type testStatus string
|
|
|
|
var (
|
|
testUnknown testStatus = "unknown"
|
|
testTableOfContents testStatus = "table-of-contents"
|
|
)
|
|
|
|
var excludeDirs = []string{
|
|
"en/about",
|
|
"zh/about",
|
|
"en/blog",
|
|
"zh/blog",
|
|
"en/boilerplates",
|
|
"zh/boilerplates",
|
|
"en/events",
|
|
"zh/events",
|
|
"en/docs/reference/glossary",
|
|
"zh/docs/reference/glossary",
|
|
"zh/news",
|
|
"en/news",
|
|
"en/test",
|
|
"zh/test",
|
|
}
|
|
|
|
type FileEntry struct {
|
|
fullPath string
|
|
relative string
|
|
url string
|
|
tested testStatus
|
|
score int
|
|
title string
|
|
owner string
|
|
notes []string
|
|
}
|
|
|
|
func main() {
|
|
var docsPath, outPath, analyticsPath string
|
|
flag.StringVar(&docsPath, "docspath", "../istio.io", "points to the cloned path of the istio.io site")
|
|
flag.StringVar(&outPath, "outpath", "out.csv", "path to create the spreadsheet CSV at")
|
|
flag.StringVar(&analyticsPath, "analyticspath", "analytics.csv", "path to a file containing the istio.io analytics CSV")
|
|
flag.Parse()
|
|
fmt.Printf("Scoring docs in %s\n", docsPath)
|
|
|
|
files, err := getAllFiles(docsPath)
|
|
if err != nil {
|
|
fmt.Printf("Error: %s", err)
|
|
}
|
|
|
|
scorers := []scorer{
|
|
getHitsScorer(analyticsPath, 15),
|
|
}
|
|
for _, scorerInstance := range scorers {
|
|
files = scorerInstance.Score(files)
|
|
}
|
|
|
|
if err := writeTestSpreadsheet(files, outPath); err != nil {
|
|
fmt.Printf("Failed to write test spreadsheet: %s\n", err.Error())
|
|
}
|
|
}
|
|
|
|
func getPriority(file FileEntry) string {
|
|
priority := 0
|
|
switch score := file.score; {
|
|
case score > maxScore*2/3:
|
|
priority = 0
|
|
case score > maxScore*1/3:
|
|
priority = 1
|
|
case score > 0:
|
|
priority = 2
|
|
case score == 0:
|
|
priority = 3
|
|
|
|
}
|
|
return fmt.Sprintf("P%d", priority)
|
|
}
|
|
|
|
func writeTestSpreadsheet(files []FileEntry, path string) error {
|
|
file, err := os.Create(path)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to create CSV: %s", err.Error())
|
|
}
|
|
defer file.Close()
|
|
|
|
writer := csv.NewWriter(file)
|
|
defer writer.Flush()
|
|
|
|
if err := writer.Write([]string{
|
|
"",
|
|
"Owner",
|
|
"Test Cases",
|
|
"Priority",
|
|
"Automated",
|
|
"In Progress",
|
|
"In Progress Last Updated",
|
|
"Done By",
|
|
"Done By Last Updated",
|
|
"GitHub Issue",
|
|
"Comments (e.g. env used)",
|
|
"Automated Sign Up",
|
|
"Automated Last Updated",
|
|
"Automation GitHub Issue",
|
|
"Generator Notes",
|
|
}); err != nil {
|
|
return fmt.Errorf("failed to write to CSV: %s", err.Error())
|
|
}
|
|
|
|
for _, value := range files {
|
|
err := writer.Write([]string{
|
|
"",
|
|
value.owner,
|
|
fmt.Sprintf("=HYPERLINK(\"%s\",\"%s\")", value.url, strings.ReplaceAll(value.title, "\"", "")),
|
|
getPriority(value),
|
|
string(value.tested),
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
strings.Join(value.notes, "\n"),
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("unable to write CSV: %s", err.Error())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getURL(path string) string {
|
|
path = strings.TrimPrefix(path, "en/")
|
|
return fmt.Sprintf("https://preliminary.istio.io/latest/%s", filepath.Dir(path))
|
|
}
|
|
|
|
func getAllFiles(path string) ([]FileEntry, error) {
|
|
files := make([]FileEntry, 0)
|
|
rootPath := path
|
|
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
|
relativePath := strings.TrimPrefix(path[len(rootPath):], "/")
|
|
if filepath.Ext(path) == ".md" && !skipFile(relativePath) {
|
|
fileContent, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to open file %s", path)
|
|
}
|
|
|
|
docTestStatus := getTestStatus(string(fileContent))
|
|
|
|
// skip table of contents as it has no commands and links and content are autogenerated from docs
|
|
if docTestStatus == testTableOfContents {
|
|
return nil
|
|
}
|
|
|
|
files = append(files, FileEntry{
|
|
fullPath: path,
|
|
relative: relativePath,
|
|
url: getURL(relativePath),
|
|
tested: docTestStatus,
|
|
title: getAndTrimField("title", string(fileContent)),
|
|
owner: getAndTrimField("owner", string(fileContent)),
|
|
notes: []string{fmt.Sprintf("Relative path:%s", relativePath)},
|
|
})
|
|
}
|
|
return nil
|
|
})
|
|
return files, err
|
|
}
|
|
|
|
func skipFile(path string) bool {
|
|
for _, value := range excludeDirs {
|
|
if strings.HasPrefix(path, value) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func getAndTrimField(field string, body string) string {
|
|
fieldRegex := regexp.MustCompile(fmt.Sprintf("%s:\\s*[\\w|\\/]*.*", field))
|
|
fieldValue := strings.TrimPrefix(fieldRegex.FindString(body), fmt.Sprintf("%s:", field))
|
|
fieldValue = strings.TrimPrefix(fieldValue, " ")
|
|
return fieldValue
|
|
}
|
|
|
|
func getTestStatus(content string) testStatus {
|
|
teststatus := getAndTrimField("test", content)
|
|
if teststatus == "" {
|
|
teststatus = string(testUnknown)
|
|
}
|
|
return testStatus(teststatus)
|
|
}
|