github.com/vlifesystems/rulehunter@v0.0.0-20180501090014-673078aa4a83/html/builder.go (about) 1 // Copyright (C) 2016-2018 vLife Systems Ltd <http://vlifesystems.com> 2 // Licensed under an MIT licence. Please see LICENSE.md for details. 3 4 package html 5 6 import ( 7 "fmt" 8 "sync" 9 "time" 10 11 "github.com/vlifesystems/rulehunter/config" 12 "github.com/vlifesystems/rulehunter/logger" 13 "github.com/vlifesystems/rulehunter/progress" 14 "github.com/vlifesystems/rulehunter/quitter" 15 ) 16 17 // Builder represents an html website builder 18 type Builder struct { 19 cfg *config.Config 20 pm *progress.Monitor 21 log logger.Logger 22 isRunning bool 23 sync.Mutex 24 } 25 26 func New( 27 cfg *config.Config, 28 pm *progress.Monitor, 29 l logger.Logger, 30 ) *Builder { 31 return &Builder{ 32 cfg: cfg, 33 pm: pm, 34 log: l, 35 isRunning: false, 36 } 37 } 38 39 // This should be run as a goroutine and it will periodically generate html 40 func (b *Builder) Run(q *quitter.Quitter) { 41 q.Add() 42 defer q.Done() 43 b.isRunning = true 44 defer func() { 45 b.Lock() 46 defer b.Unlock() 47 b.isRunning = false 48 }() 49 if err := b.generateAll(); err != nil { 50 b.log.Error(fmt.Errorf("Couldn't generate html: %s", err)) 51 } 52 53 oldExperiments := b.pm.GetExperiments() 54 55 tickChan := time.NewTicker(time.Second * 5).C 56 for { 57 select { 58 case <-tickChan: 59 currentExperiments := b.pm.GetExperiments() 60 if !areReportsUptodate(oldExperiments, currentExperiments) { 61 if err := b.generateReports(); err != nil { 62 b.log.Error(fmt.Errorf("Couldn't generate html: %s", err)) 63 } 64 } 65 if !isProgressUptodate(oldExperiments, currentExperiments) { 66 if err := b.generateProgress(); err != nil { 67 b.log.Error(fmt.Errorf("Couldn't generate html: %s", err)) 68 } 69 } 70 oldExperiments = currentExperiments 71 case <-q.C: 72 if err := b.generateAll(); err != nil { 73 b.log.Error(fmt.Errorf("Couldn't generate html: %s", err)) 74 } 75 return 76 } 77 } 78 } 79 80 // Running returns whether the html builder is running 81 func (b *Builder) Running() bool { 82 b.Lock() 83 defer b.Unlock() 84 return b.isRunning 85 } 86 87 type generator func(*config.Config, *progress.Monitor) error 88 89 func (b *Builder) generateAll() error { 90 generators := []generator{ 91 generateActivityPage, 92 generateFront, 93 generateReports, 94 generateTagPages, 95 generateCategoryPages, 96 } 97 return b.generate(generators) 98 } 99 100 func (b *Builder) generateProgress() error { 101 generators := []generator{ 102 generateActivityPage, 103 generateFront, 104 } 105 return b.generate(generators) 106 } 107 108 func (b *Builder) generateReports() error { 109 generators := []generator{ 110 generateActivityPage, 111 generateFront, 112 generateReports, 113 generateTagPages, 114 generateCategoryPages, 115 } 116 return b.generate(generators) 117 } 118 119 func (b *Builder) generate(generators []generator) error { 120 for _, g := range generators { 121 if err := g(b.cfg, b.pm); err != nil { 122 return err 123 } 124 } 125 return nil 126 } 127 128 func areReportsUptodate( 129 oldExperiments, currentExperiments []*progress.Experiment, 130 ) bool { 131 for _, ce := range currentExperiments { 132 if ce.Status.State == progress.Success && 133 time.Since(ce.Status.Stamp).Seconds() < 10 { 134 return false 135 } 136 } 137 return true 138 } 139 140 func isProgressUptodate( 141 oldExperiments, currentExperiments []*progress.Experiment, 142 ) bool { 143 if len(currentExperiments) != len(oldExperiments) { 144 return false 145 } 146 for _, ce := range currentExperiments { 147 for _, oe := range oldExperiments { 148 if !ce.IsEqual(oe) { 149 return false 150 } 151 } 152 } 153 return true 154 }