Skip to content

Commit

Permalink
StartedContext + changes to PathsToBytes and Template sending
Browse files Browse the repository at this point in the history
- Added the error and started templates which are both new txt based templates (01/02/2023 - 10:35:17)
- Renamed Context.HTML to Context.Execute as it now can call either Template.HTML or Template.Text (01/02/2023 - 10:35:51)
- Added Text as a new TemplateBufferContentType (01/02/2023 - 10:36:17)
- Moved all implementations of email.Context to contexts.go (01/02/2023 - 10:42:28)
- Template.Template is now the new parsedType which can store a reference to both a parsed HTML template and a parsed text template (01/02/2023 - 12:00:28)
- parsedTemplate.Execute will call the execute method of the set parsed template (01/02/2023 - 12:01:05)
- Renamed Template.HTML to Execute and it now can also execute text templates along with HTML templates (01/02/2023 - 12:15:17)
- Added the Template.SendAsyncAndConsume method for use cases where we want to send an email asynchronously and continue doing other stuff (01/02/2023 - 13:33:07)
- The StartedContext and ErrorContext will probably need to exist within the main package so that we can pass a ScoutState instance to them (01/02/2023 - 14:51:22)
- Added new interfaces for reading and writing to PathsToBytes. These are PathsToBytesInterface, PathsToBytesReader, PathsToBytesWriter, and PathsToBytesReadWriter. This is so I could create another type similar to the existing PathsToBytes that writes to a zip archive for email sending in StartedContext and ErrorContext (01/02/2023 - 15:55:12)
- Added the ScoutState.LoadFrom and ScoutState.SaveTo methods which can be given a PathsToBytesReader and a PathsToBytesWriter respectively to load/save a ScoutState to (01/02/2023 - 15:56:37)
- StartedContext should now be finished and ready for implementation in the Scout procedure #8 (01/02/2023 - 16:34:37)
- Added a check to Part.Encoder which stops zip attachment from being re-archived (01/02/2023 - 16:35:28)
  • Loading branch information
andygello555 committed Feb 1, 2023
1 parent 2f75ab7 commit b8ca9e1
Show file tree
Hide file tree
Showing 10 changed files with 630 additions and 316 deletions.
143 changes: 143 additions & 0 deletions email/contexts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package email

import (
"fmt"
"github.com/andygello555/game-scout/db/models"
"github.com/andygello555/gotils/v2/numbers"
"github.com/deckarep/golang-set/v2"
"github.com/volatiletech/null/v9"
"html/template"
"reflect"
"strconv"
"time"
"unicode"
)

// Context will be implemented by structures that are used to fill out a Template in Template.Execute.
type Context interface {
// Path returns the TemplatePath that this Context is for.
Path() TemplatePath
// Template returns an un-executed Template that this Context can be used for.
Template() *Template
// Funcs returns the functions that should be bound to the template.Template before parsing the HTML/Text template
// located in at the TemplatePath.
Funcs() template.FuncMap
// Execute executes the Context via Template.Execute.
Execute() *Template
// AdditionalParts returns any additional Part to add onto an Email.
AdditionalParts() []Part
}

// MeasureContext is a Context that contains the data required to fill out the Measure HTML template.
type MeasureContext struct {
Start time.Time
End time.Time
TrendingDevs []*models.TrendingDev
TopSteamApps []*models.SteamApp
DevelopersBeingDeleted []*models.TrendingDev
EnabledDevelopers int64
Config Config
}

func (m *MeasureContext) Path() TemplatePath { return Measure }
func (m *MeasureContext) Execute() *Template { return m.Template().Execute() }
func (m *MeasureContext) AdditionalParts() []Part { return []Part{} }
func (m *MeasureContext) Template() *Template { return NewParsedTemplate(HTML, m).Template(m) }
func (m *MeasureContext) Funcs() template.FuncMap {
return map[string]any{
"intRange": func(start, end, step int) []int {
return numbers.Range(start, end, step)
},
"contains": func(set []string, elem string) bool {
return mapset.NewThreadUnsafeSet(set...).Contains(elem)
},
"timePretty": func(t time.Time) string {
loc, _ := time.LoadLocation("Europe/London")
t = t.In(loc)
return numbers.Ordinal(t.Day()) + t.Format(" January 2006 at 3pm")
},
"datePretty": func(t time.Time) string {
return t.Format("02/01/2006")
},
"percentage": func(f null.Float64) string {
perc := f.Float64
if !f.IsValid() {
perc = 1.0
}
return fmt.Sprintf("%.2f%%", perc*100.0)
},
"percentageF64": func(f float64) string {
return fmt.Sprintf("%.2f%%", f*100.0)
},
"cap": func(s string) string {
r := []rune(s)
return string(append([]rune{unicode.ToUpper(r[0])}, r[1:]...))
},
"yesno": func(b bool) string {
return map[bool]string{
true: "yes",
false: "no",
}[b]
},
"inc": func(i int) int {
return i + 1
},
"dec": func(i int) int {
return i - 1
},
"div": func(a, b int) int {
return a / b
},
"ord": func(num int) string {
return numbers.OrdinalOnly(num)
},
"date": func(date time.Time) time.Time {
year, month, day := date.Date()
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
},
"duration": func(duration models.NullDuration) time.Duration {
return time.Duration(duration.Int64)
},
"timeSub": func(t1 time.Time, t2 time.Time) time.Duration {
return t1.Sub(t2)
},
"days": func(d time.Duration) int {
return int(d.Hours() / 24)
},
"lastIndex": func(a any) int {
return reflect.ValueOf(a).Len() - 1
},
"trunc": func(s string, max int) string {
lastSpaceIx := -1
sLen := 0
for i, r := range s {
if unicode.IsSpace(r) {
lastSpaceIx = i
}
sLen++
if sLen >= max {
if lastSpaceIx != -1 {
return s[:lastSpaceIx] + "..."
}
// If here, string is longer than max, but has no spaces
}
}
return s
},
"float": func(f float64) string {
return strconv.FormatFloat(f, 'G', 12, 64)
},
}
}

type ErrorContext struct {
}

func (e *ErrorContext) Path() TemplatePath { return Error }
func (e *ErrorContext) Execute() *Template { return e.Template().Execute() }
func (e *ErrorContext) AdditionalParts() []Part { return []Part{} }
func (e *ErrorContext) Template() *Template { return NewParsedTemplate(Text, e).Template(e) }
func (e *ErrorContext) Funcs() template.FuncMap {
//TODO implement me
panic("implement me")
}
2 changes: 1 addition & 1 deletion email/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (p *Part) ContentTypeSlug() string {
func (p *Part) Encoder() (io.WriteCloser, error) {
_, _ = p.file.Seek(0, io.SeekStart)
if p.Attachment {
if p.Buffer.Size() > compressionLimit {
if p.Buffer.Size() > compressionLimit && p.ContentType != "application/zip" {
p.ContentType = "application/zip"
filename := p.Filename
p.Filename = strings.TrimSuffix(filename, filepath.Ext(filename)) + ".zip"
Expand Down
7 changes: 4 additions & 3 deletions email/email_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func TestMeasureContext_Template(t *testing.T) {
var err error
for testNo, test := range examples {
testNo++
template := test.context.HTML()
template := test.context.Execute()
if err = template.Error; err != nil {
if test.err != nil {
if test.err.Error() != err.Error() {
Expand Down Expand Up @@ -333,7 +333,7 @@ func TestTemplate_SendAsync(t *testing.T) {
for testNo, test := range examples {
testNo++

template := test.context.HTML()
template := test.context.Execute()
resp := sendAsyncTemplate(template)
checkResponseError(t, testNo, test, resp)
checkMessageRequest(t, testNo, test, 1, server)
Expand All @@ -352,7 +352,7 @@ func TestTemplate_SendSync(t *testing.T) {
for testNo, test := range examples {
testNo++

template := test.context.HTML()
template := test.context.Execute()
resp := template.SendSync()
checkResponseError(t, testNo, test, resp)
checkMessageRequest(t, testNo, test, 1, server)
Expand Down Expand Up @@ -403,6 +403,7 @@ func ExampleNewEmail() {
fmt.Printf("Final email size: %d\n", email.Size())
fmt.Println(email.Profiling.String())

// Convert DOS line-endings to UNIX so that we can check the output.
fmt.Println(strings.ReplaceAll(email.Read().String(), "\r\n", "\n"))
// Output:
// Final email size: 463
Expand Down

0 comments on commit b8ca9e1

Please sign in to comment.