From 51eb72bdaf723f61d5b7e157a173f53e2838954d Mon Sep 17 00:00:00 2001 From: Inanc Gumus Date: Mon, 26 Aug 2019 17:19:27 +0300 Subject: [PATCH] update: functional log parser --- advfuncs/logparser/01/errdump.go | 19 ++++++ advfuncs/logparser/01/filters.go | 38 ++++++++++++ advfuncs/logparser/01/log.txt | 16 +++++ advfuncs/logparser/01/log_err_missing.txt | 16 +++++ advfuncs/logparser/01/log_err_negative.txt | 16 +++++ advfuncs/logparser/01/log_err_str.txt | 16 +++++ advfuncs/logparser/01/main.go | 38 ++++++++++++ advfuncs/logparser/01/output.go | 27 ++++++++ advfuncs/logparser/01/parser.go | 65 ++++++++++++++++++++ advfuncs/logparser/01/update.go | 33 ++++++++++ advfuncs/logparser/functional/chartwriter.go | 14 ++--- advfuncs/new/01-intro/funcval/main.go | 28 +++++++++ advfuncs/new/01-intro/passfunc/main.go | 53 ++++++++++++++++ 13 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 advfuncs/logparser/01/errdump.go create mode 100644 advfuncs/logparser/01/filters.go create mode 100644 advfuncs/logparser/01/log.txt create mode 100644 advfuncs/logparser/01/log_err_missing.txt create mode 100644 advfuncs/logparser/01/log_err_negative.txt create mode 100644 advfuncs/logparser/01/log_err_str.txt create mode 100644 advfuncs/logparser/01/main.go create mode 100644 advfuncs/logparser/01/output.go create mode 100644 advfuncs/logparser/01/parser.go create mode 100644 advfuncs/logparser/01/update.go create mode 100644 advfuncs/new/01-intro/funcval/main.go create mode 100644 advfuncs/new/01-intro/passfunc/main.go diff --git a/advfuncs/logparser/01/errdump.go b/advfuncs/logparser/01/errdump.go new file mode 100644 index 0000000..a55bf46 --- /dev/null +++ b/advfuncs/logparser/01/errdump.go @@ -0,0 +1,19 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import "fmt" + +// dumpErrs together to simplify multiple error handling +func dumpErrs(errs []error) { + for _, err := range errs { + if err != nil { + fmt.Println("> Err:", err) + } + } +} diff --git a/advfuncs/logparser/01/filters.go b/advfuncs/logparser/01/filters.go new file mode 100644 index 0000000..01594b3 --- /dev/null +++ b/advfuncs/logparser/01/filters.go @@ -0,0 +1,38 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import "strings" + +func filter(r result) bool { + return filterOrg(r) +} + +func filterOrg(r result) bool { + return strings.HasSuffix(r.domain, ".org") +} + +func filterBlogs(r result) bool { + return strings.HasPrefix(r.domain, "blog.") +} + +// type filterFunc func(result) bool + +// func filter(r result, process filterFunc) bool { +// return process(r) +// } + +// func noopFilter(r result) bool { +// return true +// } + +// func notUsing(filter filterFunc) filterFunc { +// return func(r result) bool { +// return !filter(r) +// } +// } diff --git a/advfuncs/logparser/01/log.txt b/advfuncs/logparser/01/log.txt new file mode 100644 index 0000000..940b832 --- /dev/null +++ b/advfuncs/logparser/01/log.txt @@ -0,0 +1,16 @@ +learngoprogramming.com 10 +learngoprogramming.com 15 +learngoprogramming.com 10 +learngoprogramming.com 20 +learngoprogramming.com 5 +golang.org 40 +golang.org 20 +golang.org 45 +golang.org 15 +blog.golang.org 60 +blog.golang.org 30 +blog.golang.org 20 +blog.golang.org 65 +blog.golang.org 15 +inanc.io 30 +inanc.io 70 \ No newline at end of file diff --git a/advfuncs/logparser/01/log_err_missing.txt b/advfuncs/logparser/01/log_err_missing.txt new file mode 100644 index 0000000..9169c91 --- /dev/null +++ b/advfuncs/logparser/01/log_err_missing.txt @@ -0,0 +1,16 @@ +learngoprogramming.com 10 +learngoprogramming.com 15 +learngoprogramming.com 10 +learngoprogramming.com 20 +learngoprogramming.com +golang.org 40 +golang.org 20 +golang.org 45 +golang.org 15 +blog.golang.org 60 +blog.golang.org 30 +blog.golang.org 20 +blog.golang.org 65 +blog.golang.org 15 +inanc.io 30 +inanc.io 70 \ No newline at end of file diff --git a/advfuncs/logparser/01/log_err_negative.txt b/advfuncs/logparser/01/log_err_negative.txt new file mode 100644 index 0000000..feccc0c --- /dev/null +++ b/advfuncs/logparser/01/log_err_negative.txt @@ -0,0 +1,16 @@ +learngoprogramming.com 10 +learngoprogramming.com 15 +learngoprogramming.com 10 +learngoprogramming.com 20 +learngoprogramming.com 5 +golang.org 40 +golang.org 20 +golang.org -50 +golang.org 15 +blog.golang.org 60 +blog.golang.org 30 +blog.golang.org 20 +blog.golang.org 65 +blog.golang.org 15 +inanc.io 30 +inanc.io 70 \ No newline at end of file diff --git a/advfuncs/logparser/01/log_err_str.txt b/advfuncs/logparser/01/log_err_str.txt new file mode 100644 index 0000000..59a1fd8 --- /dev/null +++ b/advfuncs/logparser/01/log_err_str.txt @@ -0,0 +1,16 @@ +learngoprogramming.com 10 +learngoprogramming.com 15 +learngoprogramming.com 10 +learngoprogramming.com 20 +learngoprogramming.com 5 +golang.org FORTY +golang.org 20 +golang.org 45 +golang.org 15 +blog.golang.org 60 +blog.golang.org 30 +blog.golang.org 20 +blog.golang.org 65 +blog.golang.org 15 +inanc.io 30 +inanc.io 70 \ No newline at end of file diff --git a/advfuncs/logparser/01/main.go b/advfuncs/logparser/01/main.go new file mode 100644 index 0000000..505f5c9 --- /dev/null +++ b/advfuncs/logparser/01/main.go @@ -0,0 +1,38 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "bufio" + "os" +) + +/* +fmt.Println(strings.Map(unpunct, "hello!!! HOW ARE YOU???? :))")) +fmt.Println(strings.Map(unpunct, "TIME IS UP!")) + +func unpunct(r rune) rune { + if unicode.IsPunct(r) { + return -1 + } + return unicode.ToLower(r) +} +*/ + +func main() { + p := newParser() + + in := bufio.NewScanner(os.Stdin) + for in.Scan() { + r := parse(p, in.Text()) // TODO: parsed -> r + update(p, r) + } + + summarize(p) + dumpErrs([]error{in.Err(), err(p)}) +} diff --git a/advfuncs/logparser/01/output.go b/advfuncs/logparser/01/output.go new file mode 100644 index 0000000..2a5eb17 --- /dev/null +++ b/advfuncs/logparser/01/output.go @@ -0,0 +1,27 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "sort" + "strings" +) + +// summarize the parsing results +func summarize(p *parser) { + sort.Strings(p.domains) + + fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS") + fmt.Println(strings.Repeat("-", 45)) + + for _, domain := range p.domains { + fmt.Printf("%-30s %10d\n", domain, p.sum[domain].visits) + } + fmt.Printf("\n%-30s %10d\n", "TOTAL", p.total) +} diff --git a/advfuncs/logparser/01/parser.go b/advfuncs/logparser/01/parser.go new file mode 100644 index 0000000..17bf22e --- /dev/null +++ b/advfuncs/logparser/01/parser.go @@ -0,0 +1,65 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "strconv" + "strings" +) + +// result stores details about a log line +type result struct { + domain string + visits int + // add more metrics when needed +} + +// parser keep tracks of the parsing +type parser struct { + sum map[string]result // metrics per domain + domains []string // unique domain names + total int // total visits for all domains + lines int // number of parsed lines (for the error messages) + lerr error // the last error occurred +} + +// newParser creates and returns a new parser +func newParser() *parser { + return &parser{sum: make(map[string]result)} +} + +// update the parsing results +func parse(p *parser, line string) (r result) { + if p.lerr != nil { + return + } + + p.lines++ + + fields := strings.Fields(line) + if len(fields) != 2 { + p.lerr = fmt.Errorf("wrong input: %v (line #%d)", fields, p.lines) + return + } + + var err error + + r.domain = fields[0] + r.visits, err = strconv.Atoi(fields[1]) + + if r.visits < 0 || err != nil { + p.lerr = fmt.Errorf("wrong input: %q (line #%d)", fields[1], p.lines) + } + return +} + +// err returns the last error encountered +func err(p *parser) error { + return p.lerr +} diff --git a/advfuncs/logparser/01/update.go b/advfuncs/logparser/01/update.go new file mode 100644 index 0000000..173ee1a --- /dev/null +++ b/advfuncs/logparser/01/update.go @@ -0,0 +1,33 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +// update updates all the parsing results using the given parsing result +func update(p *parser, r result) { + if p.lerr != nil { + return + } + + if !filter(r) { + return + } + + // Collect the unique domains + if _, ok := p.sum[r.domain]; !ok { + p.domains = append(p.domains, r.domain) + } + + // Keep track of total and per domain visits + p.total += r.visits + + // create and assign a new copy of `visit` + p.sum[r.domain] = result{ + domain: r.domain, + visits: r.visits + p.sum[r.domain].visits, + } +} diff --git a/advfuncs/logparser/functional/chartwriter.go b/advfuncs/logparser/functional/chartwriter.go index d21a200..2c3d3c1 100644 --- a/advfuncs/logparser/functional/chartwriter.go +++ b/advfuncs/logparser/functional/chartwriter.go @@ -5,7 +5,7 @@ import ( "sort" "strconv" - c "github.com/wcharczuk/go-chart" + "github.com/wcharczuk/go-chart" ) func chartWriter(w io.Writer) outputFn { @@ -19,22 +19,22 @@ func chartWrite(w io.Writer, res []result) error { return res[i].domain > res[j].domain }) - donut := c.DonutChart{ + donut := chart.DonutChart{ Title: "Total Visits Per Domain", - TitleStyle: c.Style{ + TitleStyle: chart.Style{ FontSize: 35, Show: true, - FontColor: c.ColorAlternateGreen, + FontColor: chart.ColorAlternateGreen, }, Width: 1920, Height: 800, } for _, r := range res { - v := c.Value{ + v := chart.Value{ Label: r.domain + r.page + ": " + strconv.Itoa(r.visits), Value: float64(r.visits), - Style: c.Style{ + Style: chart.Style{ FontSize: 14, }, } @@ -42,5 +42,5 @@ func chartWrite(w io.Writer, res []result) error { donut.Values = append(donut.Values, v) } - return donut.Render(c.SVG, w) + return donut.Render(chart.SVG, w) } diff --git a/advfuncs/new/01-intro/funcval/main.go b/advfuncs/new/01-intro/funcval/main.go new file mode 100644 index 0000000..e84b1a6 --- /dev/null +++ b/advfuncs/new/01-intro/funcval/main.go @@ -0,0 +1,28 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "strings" +) + +func main() { + fmt.Printf("HasPrefix: %T\n", strings.HasPrefix) + fmt.Printf("HasSuffix: %T\n", strings.HasSuffix) + fmt.Println() + + var fn func(string, string) bool + + fn = strings.HasPrefix + fn = strings.HasSuffix + + ok := fn("gopher", "go") + + fmt.Printf("ok : %t\n", ok) +} diff --git a/advfuncs/new/01-intro/passfunc/main.go b/advfuncs/new/01-intro/passfunc/main.go new file mode 100644 index 0000000..842b35f --- /dev/null +++ b/advfuncs/new/01-intro/passfunc/main.go @@ -0,0 +1,53 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "unicode" +) + +func main() { + s := "Func Values Are Awesome..." + + // s = strings.Map(unicode.ToUpper, s) + // s = strings.Map(unicode.ToLower, s) + // s = strings.Map(excite, s) + // s = strings.Map(calm, s) + + // fmt.Printf("%T\n", strings.Map) + s = mapx(calm, s) + + fmt.Println(s) +} + +func mapx(mapping func(rune) rune, s string) string { + var ns []rune + + for _, r := range s { + if r = mapping(r); r == -1 { + continue + } + ns = append(ns, r) + } + return string(ns) +} + +func calm(r rune) rune { + if unicode.IsPunct(r) { + return -1 + } + return unicode.ToLower(r) +} + +func excite(r rune) rune { + if r == '.' { + return '!' + } + return unicode.ToTitle(r) +}