Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

14 convert time slot to a config #19

Merged
merged 6 commits into from
Dec 6, 2022
Merged
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
5 changes: 4 additions & 1 deletion config.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ content:
Programs:
- Name: "gettysburg10"
Type: "file"
Timeslot: "afternoon"
Source: "./static/gettysburg10.wav"
Timeslot:
Begin: "11:00PM"
End: "11:30PM"

7 changes: 0 additions & 7 deletions config_sample.yml

This file was deleted.

2 changes: 1 addition & 1 deletion content/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
type Program struct {
Name string
Source string
Timeslot Timeslot
Timeslot *Timeslot
Type MediaType
}

Expand Down
66 changes: 42 additions & 24 deletions content/program_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,69 @@ func TestProgram_GetMedia(t *testing.T) {
name: "Success: returns folder",
fields: fields{
program: &Program{
Name: "David Rovics Folder",
Source: "./static/david_rovics",
Timeslot: "early",
Type: "folder",
Name: "David Rovics Folder",
Source: "./static/david_rovics",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: "folder",
},
},
want: (&Program{
Name: "David Rovics Folder",
Source: "./static/david_rovics",
Timeslot: "early",
Type: "folder",
Name: "David Rovics Folder",
Source: "./static/david_rovics",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: "folder",
}).GetMedia(),
},
{
name: "Success: returns file",
fields: fields{
program: &Program{
Name: "Piano Six Seconds",
Source: "./static/piano_six_seconds.mp3",
Timeslot: "afternoon",
Type: "file",
Name: "Piano Six Seconds",
Source: "./static/piano_six_seconds.mp3",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: "file",
},
},
want: (&Program{
Name: "Piano Six Seconds",
Source: "./static/piano_six_seconds.mp3",
Timeslot: "afternoon",
Type: "file",
Name: "Piano Six Seconds",
Source: "./static/piano_six_seconds.mp3",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: "file",
}).GetMedia(),
},
{
name: "Success: returns web radio",
fields: fields{
program: &Program{
Name: "Indie Pop Rocks",
Source: "https://somafm.com/indiepop.pls",
Timeslot: "any",
Type: "web_radio",
Name: "Indie Pop Rocks",
Source: "https://somafm.com/indiepop.pls",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: "web_radio",
},
},
want: (&Program{
Name: "Indie Pop Rocks",
Source: "https://somafm.com/indiepop.pls",
Timeslot: "any",
Type: "web_radio",
Name: "Indie Pop Rocks",
Source: "https://somafm.com/indiepop.pls",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: "web_radio",
}).GetMedia(),
},
{
Expand Down
73 changes: 21 additions & 52 deletions content/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,18 @@ import (
"time"
)

const (
Early Timeslot = "early"
Morning Timeslot = "morning"
Breakfast Timeslot = "breakfast"
Midmorning Timeslot = "midmorning"
Afternoon Timeslot = "afternoon"
Commute Timeslot = "commute"
Evening Timeslot = "evening"
Late Timeslot = "late"
Overnight Timeslot = "overnight"
All Timeslot = "all"
)

type Timeslot string

type Slot struct {
Begin string
End string
}

var Shuffled bool

var TimeslotMap = map[Timeslot]*Slot{
Early: {"4:00 AM", "6:00 AM"},
Morning: {"6:00 AM", "8:00 AM"},
Breakfast: {"8:00 AM", "11:00 AM"},
Midmorning: {"11:00 AM", "2:00 PM"},
Afternoon: {"2:00 PM", "5:00 PM"},
Commute: {"5:00 PM", "7:00 PM"},
Evening: {"7:00 PM", "11:00 PM"},
Late: {"11:00 PM", "2:00 AM"},
Overnight: {"2:00 AM", "4:00 AM"},
All: {"12:00 AM", "12:00 PM"},
}

type Scheduler struct {
Content struct {
Programs []*Program
}
}

func (s *Scheduler) Run() error {
func (s *Scheduler) Run(currentTime time.Time) error {
log.Info("Starting Daemon")

log.Infof("Press ESC to quit")
// set up the loop to continuously check for key entries
now := time.Now()
ts := getTimeSlot(&now)
// if randomized mode do x

// setup signal listeners
sigchnl := make(chan os.Signal, 1)
Expand All @@ -70,31 +33,37 @@ func (s *Scheduler) Run() error {

// check content from scheduler and run through it
for _, p := range s.Content.Programs {
now := currentTime
log.Debugf("program %v", formatter.StructToIndentedString(p))
// Check Timeslots
if ts == p.Timeslot || ts == All {

if p.Timeslot.IsScheduledNow(now) {
log.Infof("getting media type: %v", p.Type)
content := p.GetMedia()
log.Debugf("media struct: %v", content)
content.Get()
err := content.Get() // retrieve contents from file
if err != nil {
return err
}
go func() {
for {
stop := <-sigchnl
s.Stop(stop, content)
}
}()
err := content.Play()
err = content.Play()
if err != nil {
return err
} // play will block until done
}

if !p.Timeslot.IsScheduledNow(now) {
log.WithField("IsScheduledNow", p.Timeslot.IsScheduledNow(now)).
WithField("current time", time.Now().
Format(time.Kitchen)).Infof("media not scheduled")
}
// TODO make these checks run in a loop and always check if programs should be playing
}
// if radio station start 30 minute counter.
// smartly allocate programs to timeslots based on length if known
// if time between TimeSlotMap do x
// play program from that slot.
// wait for program to finish
// get next

exitcode := <-exitchnl
os.Exit(exitcode)
return nil
Expand Down Expand Up @@ -154,10 +123,10 @@ func (s *Scheduler) Stop(signal os.Signal, media Media) {
}
}

func getTimeSlot(t *time.Time) Timeslot {
// if t between certain times return Timeslot
return All
}
//func getTimeSlot(t *time.Time) Timeslot {
// // if t between certain times return Timeslot
// return All
//}

func NewScheduler(file string) (*Scheduler, error) {
log.Info("Loading Config File from: ", file)
Expand Down
15 changes: 8 additions & 7 deletions content/schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
)

func TestNewScheduler(t *testing.T) {
t.Parallel()
type args struct {
file string
}
Expand All @@ -30,10 +29,13 @@ func TestNewScheduler(t *testing.T) {
scheduler: &Scheduler{
Content: struct{ Programs []*Program }{Programs: []*Program{
{
Name: "gettysburg10",
Source: "./static/gettysburg10.wav",
Timeslot: Timeslot("afternoon"),
Type: MediaType("file"),
Name: "gettysburg10",
Source: "./static/gettysburg10.wav",
Timeslot: &Timeslot{
Begin: "11:00PM",
End: "11:30PM",
},
Type: MediaType("file"),
},
}},
},
Expand All @@ -57,10 +59,9 @@ func TestNewScheduler(t *testing.T) {
wantErr: true,
},
}
// TODO make test pass when running in parallel and troubleshoot race condition.
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := NewScheduler(tt.args.file)
if err != nil && tt.wantErr {
assert.Error(t, err)
Expand Down
46 changes: 46 additions & 0 deletions content/timeslot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package content

import (
"github.com/araddon/dateparse"
log "github.com/sirupsen/logrus"
"strconv"
"time"
)

// Times represents timeslots and are parsed in a 24hour format
type Timeslot struct {
Begin string
End string
}

// IsScheduledNow checks the current time and returns a bool if the time falls within the range
func (t *Timeslot) IsScheduledNow(current time.Time) bool {
// get date info for string
date := time.Date(current.Year(), current.Month(), current.Day(), 0, 0, 0, 0, time.Local)
year, month, day := date.Date()

// convert ints to dateString
dateString := strconv.Itoa(year) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(day)

// parse the date and the config time
// parsed times are returned in 2022-12-05 15:05:00 +0000 UTC format which doesn't appear to have a const in the time package
parsedStartTime, _ := dateparse.ParseAny(dateString + " " + t.Begin)
parsedEndTime, _ := dateparse.ParseAny(dateString + " " + t.End)

// matched parse time to fixed zone time
startTime := time.Date(parsedStartTime.Year(), parsedStartTime.Month(), parsedStartTime.Day(), parsedStartTime.Hour(), parsedStartTime.Minute(), parsedStartTime.Second(), parsedStartTime.Nanosecond(), time.Local)
endTime := time.Date(parsedEndTime.Year(), parsedEndTime.Month(), parsedEndTime.Day(), parsedEndTime.Hour(), parsedEndTime.Minute(), parsedEndTime.Second(), parsedEndTime.Nanosecond(), time.Local)

return inTimeSpan(startTime, endTime, current)
}

func inTimeSpan(start, end, current time.Time) bool {
log.WithField("start", start).WithField("current", current).WithField("end", end).Info("timeslot::inTimeSpan: configured times")
// handle scheduling that traverses days.
tz, _ := time.LoadLocation("UTC")
if end.Before(start) && current.After(start) {
return true
}

return current.In(tz).After(start) && current.In(tz).Before(end)
}
73 changes: 73 additions & 0 deletions content/timeslot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package content

import (
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"testing"
"time"
)

type TimeProvider interface {
Now() time.Time
}

type testTime struct {
TimeProvider
}

func (testTime *testTime) Now() time.Time {
tz, _ := time.LoadLocation("EST")
now := time.Date(2022, 12, 05, 23, 27, 0, 0, tz)
log.Infof("testTime %v", now)
return now
}

func TestTimes_IsScheduledNow(t *testing.T) {
t.Parallel()
type fields struct {
Current time.Time
Begin string
End string
}
tests := []struct {
name string
fields fields
want bool
}{
{
name: "Returns True",
fields: fields{
Begin: "11:00 PM",
End: "11:59 PM",
},
want: true,
},
{
name: "Returns False",
fields: fields{
Begin: "11:28 PM",
End: "10:47 PM",
},
want: false,
},
{
name: "Success: evaluates true for times that traverse days",
fields: fields{
Begin: "11:00 PM",
End: "2:30 AM",
},
want: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t1 *testing.T) {
t1.Parallel()
t := &Timeslot{
Begin: tt.fields.Begin,
End: tt.fields.End,
}
assert.Equalf(t1, tt.want, t.IsScheduledNow((&testTime{}).Now()), "IsScheduledNow()")
})
}
}
Loading