Skip to content
This repository was archived by the owner on Jun 13, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .idea/codetest.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions .idea/libraries/GOPATH__codetest_.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

561 changes: 561 additions & 0 deletions .idea/workspace.xml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ ln -s `pwd`/hooks/pre-commit .git/hooks
```bash
go get -v ./... && go build -v
```

# Running the client
Available in client/ dir. `go run main.go` will use defaults that work on most systems, otherwise arguments are available.
* `/` is a list that links to the key/ template
* `/key/RESULT.KEY`
7 changes: 7 additions & 0 deletions client/draws/consume.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package draws

// Fetcher fetches results
type Fetcher interface {
GetAll() ([]Result, error)
ByKey(key string) (Result, error)
}
160 changes: 160 additions & 0 deletions client/draws/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package draws

// Response is the response from the feed
type Reponse struct {
Results []Result `json:"result"`
Messages []interface{} `json:"messages"`
}

// Result is the key result
type Result struct {
Type string `json:"type"`
Key string `json:"key"`
Name string `json:"name"`
Autoplayable string `json:"autoplayable"`
GameTypes []GameTypes `json:"game_types,omitempty"`
Draws []Draws `json:"draws,omitempty"`
Days []Day `json:"days,omitempty"`
Addons []interface{} `json:"addons,omitempty"`
QuickpickSizes []int `json:"quickpick_sizes,omitempty"`
Lottery Lottery `json:"lottery,omitempty"`
Draw Draw `json:"draw,omitempty"`
}

// Draw is the type of draw
type Draw struct {
Name string `json:"name"`
Description string `json:"description"`
DrawNumber int `json:"draw_number"`
DrawStop string `json:"draw_stop"`
DrawDate string `json:"draw_date"`
Prize Prize `json:"prize"`
Offers []Offer `json:"offers"`
TermsAndConditionsURL string `json:"terms_and_conditions_url"`
}

// Offer is the offer details
type Offer struct {
Name string `json:"name"`
Key string `json:"key"`
NumTickets int `json:"num_tickets"`
Price Price `json:"price"`
PricePerTicket Price `json:"price_per_ticket"`
Ribbon string `json:"ribbon"`
BonusPrize interface{} `json:"bonus_prize"`
}

// Price is the cost
type Price struct {
Amount string `json:"amount"`
Currency string `json:"currency"`
}

// Prize is the details of the prize
type Prize struct {
Type string `json:"type"`
CardTitle string `json:"card_title"`
Name string `json:"name"`
Description string `json:"description"`
Content PrizeContent `json:"content"`
Value Price `json:"value"`
ValueIsExact bool `json:"value_is_exact"`
HeroImage interface{} `json:"hero_image"`
CarouselImages []string `json:"carousel_images"`
FeatureDrawImage interface{} `json:"feature_draw_image"`
EdmImage string `json:"edm_image"`
}

// PrizeContent is for details for customer
type PrizeContent struct {
SalesPitchHeading1 string `json:"sales_pitch_heading_1"`
SalesPitchSubHeading1 string `json:"sales_pitch_sub_heading_1"`
Paragraph1 string `json:"paragraph_1"`
Paragraph2 string `json:"paragraph_2"`
Paragraph3 string `json:"paragraph_3"`
Image string `json:"image"`
SalesPitchHeading2 string `json:"sales_pitch_heading_2"`
SalesPitchSubHeading2 string `json:"sales_pitch_sub_heading_2"`
Features []string `json:"features"`
}

// Draws is the draws available
type Draws struct {
Name interface{} `json:"name"`
Date string `json:"date"`
Stop string `json:"stop"`
DrawNo int `json:"draw_no"`
PrizePool Price `json:"prize_pool"`
JackpotImage JackpotImage `json:"jackpot_image"`
}

// JackpotImage is the image for the jackpot
type JackpotImage struct {
ImageName string `json:"image_name"`
ImageURL string `json:"image_url"`
SvgURL string `json:"svg_url"`
ImageWidth int `json:"image_width"`
ImageHeight int `json:"image_height"`
ContentDescription string `json:"content_description"`
}

// GameTypes is the type of games
type GameTypes struct {
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
GameOffers []GameOffer `json:"game_offers"`
}

// GameOffer is the offer for the game
type GameOffer struct {
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
Price Price `json:"price"`
MinGames int `json:"min_games"`
MaxGames int `json:"max_games"`
Multiple int `json:"multiple"`
Ordered bool `json:"ordered"`
GameIncrement GameIncrement `json:"game_increment"`
EquivalentGames int `json:"equivalent_games"`
NumberSets []NumberSet `json:"number_sets"`
DisplayRange interface{} `json:"display_range"`
}

// NumberSet is the number set
type NumberSet struct {
First int `json:"first"`
Last int `json:"last"`
Sets []Set `json:"sets"`
}

// Set is the set
type Set struct {
Name string `json:"name"`
Count int `json:"count"`
}

// GameIncrement is the increment for the game
type GameIncrement struct {
Num4 int `json:"4"`
}

// Lottery is the lottery detials
type Lottery struct {
ID string `json:"id"`
Name string `json:"name"`
Desc string `json:"desc"`
Multidraw bool `json:"multidraw"`
Type string `json:"type"`
IconURL string `json:"icon_url"`
IconWhiteURL string `json:"icon_white_url"`
PlayURL string `json:"play_url"`
LotteryID int `json:"lottery_id"`
}

// Day is the day for the result
type Day struct {
Name string `json:"name"`
Value int `json:"value"`
}
73 changes: 73 additions & 0 deletions client/draws/live-cached.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package draws

import (
"fmt"
"github.com/sirupsen/logrus"
"sync"
"time"
)

// Cache caches another Fetchers results - if more complex data feed, or larger data set, an ARC mechanism might be useful. E.g. hashicorps
type Cache struct {
sync.RWMutex
Feed Fetcher
Cache map[string]Result
Logger *logrus.Logger
}

// Update updates the cache
func (c Cache) Update() error {
res, err := c.Feed.GetAll()
if err != nil {
return err
}
c.Lock()
for _, r := range res {
c.Cache[r.Key] = r
}
c.Unlock()
return nil
}

// Monitor ticks every 30 seconds to trigger a cache update. This could be done with a retry-backoff algorithm as well.
func (c Cache) Monitor() {
for range time.Tick(time.Second * 30) {
err := c.Update()
if err != nil {
c.Logger.Error(err)
}
}
}

// GetAll returns all Results in the cache
func (c Cache) GetAll() (res []Result, err error) {
c.RLock()
for _, r := range c.Cache {
res = append(res, r)
}
c.RUnlock()
return
}

// ByKey returns a result by key
func (c Cache) ByKey(key string) (res Result, err error) {
c.RLock()
defer c.RUnlock()
res, ok := c.Cache[key]
if !ok {
err = fmt.Errorf("No result found for key: %s", key)
}
return
}

// NewCachedFeed returns a cached feed fetcher
func NewCachedFeed(feed Fetcher, logger *logrus.Logger) Fetcher {
c := Cache{
Cache: map[string]Result{},
Feed: feed,
Logger: logger,
}
c.Update()
go c.Monitor()
return c
}
62 changes: 62 additions & 0 deletions client/draws/live.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package draws

import (
"encoding/json"
"fmt"
"github.com/sirupsen/logrus"
"net/http"
)

// ResultsLiveFeed is a fetcher that works directly on the external feed
type ResultsLiveFeed struct {
FeedServerPath string
Logger *logrus.Logger
}

// getData fetches the data from the feed
func (r ResultsLiveFeed) getData() (jsonData Reponse, err error) {
resp, err := http.Get(r.FeedServerPath)
if err != nil {
return
}

jsonData = Reponse{}
err = json.NewDecoder(resp.Body).Decode(&jsonData)
return
}

// GetAll returns all results
func (r ResultsLiveFeed) GetAll() (res []Result, err error) {
jsonData, err := r.getData()
res = jsonData.Results
return
}

// ByKey returns a Result by key
func (r ResultsLiveFeed) ByKey(key string) (res Result, err error) {
resp, err := http.Get(r.FeedServerPath)
if err != nil {
return
}

jsonData := Reponse{}
err = json.NewDecoder(resp.Body).Decode(&jsonData)
if err != nil {
return
}

for _, r := range jsonData.Results {
if r.Key == key {
return r, nil
}
}
return Result{}, fmt.Errorf("No result found for key: %s", key)
}

// NewLiveFeed returns a new live feed fetcher
func NewLiveFeed(feedServerPath string, logger *logrus.Logger) Fetcher {
return ResultsLiveFeed{
FeedServerPath: feedServerPath,
Logger: logger,
}
}
Loading