Skip to content

Commit

Permalink
implement get-before/-after for basic event switching
Browse files Browse the repository at this point in the history
  • Loading branch information
ja-he committed Jul 28, 2024
1 parent 3b6cd04 commit 7e1ca65
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 18 deletions.
78 changes: 72 additions & 6 deletions internal/control/cli/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1963,18 +1963,84 @@ func (c *Controller) getCurrentMonthEvents() ([]*model.Event, error) {
return events, nil
}

func (c *Controller) ensureCurrentEventVisible() {
e := c.data.CurrentEvent
if e != nil {
c.ensureEventsPaneTimestampWithinVisibleScroll(e.Start)
c.ensureEventsPaneTimestampWithinVisibleScroll(e.End)
}
}

func (c *Controller) switchToNextEventInDay() {
if c.data.CurrentEvent != nil {
c.ensureEventsPaneTimestampWithinVisibleScroll(c.data.CurrentEvent.Start)
c.ensureEventsPaneTimestampWithinVisibleScroll(c.data.CurrentEvent.End)
defer c.ensureCurrentEventVisible()

if c.data.CurrentEvent == nil {
candidate, err := c.dataProvider.GetEventAfter(c.data.CurrentDate.ToGotime())
if err != nil {
c.log.Error().Err(err).Stringer("date", c.data.CurrentDate).Msg("could not get next for current date")
return
}
if model.DateFromGotime(candidate.Start) == c.data.CurrentDate {
c.data.CurrentEvent = candidate
c.log.Debug().Stringer("event", candidate).Msg("switched to next event")
return
}
c.log.Debug().Msg("no event on current day")
return
}

next, err := c.dataProvider.GetEventAfter(c.data.CurrentEvent.End)
if err != nil {
c.log.Error().Err(err).Stringer("date", c.data.CurrentDate).Msg("could not get next for current date")
return
}
if next == nil {
c.log.Info().Msg("there is no next event")
return
}
if model.DateFromGotime(next.Start) != c.data.CurrentDate {
c.log.Info().Msg("next event is on a different day")
return
}

c.data.CurrentEvent = next
c.log.Debug().Stringer("event", next).Msg("switched to next event")
}

func (c *Controller) switchToPreviousEventInDay() {
if c.data.CurrentEvent != nil {
c.ensureEventsPaneTimestampWithinVisibleScroll(c.data.CurrentEvent.Start)
c.ensureEventsPaneTimestampWithinVisibleScroll(c.data.CurrentEvent.End)
defer c.ensureCurrentEventVisible()

if c.data.CurrentEvent == nil {
candidate, err := c.dataProvider.GetEventBefore(c.data.CurrentDate.ToGotime().Add(24 * time.Hour))
if err != nil {
c.log.Error().Err(err).Stringer("date", c.data.CurrentDate).Msg("could not get prev for current date")
return
}
if model.DateFromGotime(candidate.Start) == c.data.CurrentDate {
c.data.CurrentEvent = candidate
c.log.Debug().Stringer("event", candidate).Msg("switched to prev event")
return
}
c.log.Debug().Msg("no event on current day")
return
}

prev, err := c.dataProvider.GetEventBefore(c.data.CurrentEvent.Start)
if err != nil {
c.log.Error().Err(err).Stringer("date", c.data.CurrentDate).Msg("could not get prev for current date")
return
}
if prev == nil {
c.log.Info().Msg("there is no prev event")
return
}
if model.DateFromGotime(prev.Start) != c.data.CurrentDate {
c.log.Info().Msg("prev event is on a different day")
return
}

c.data.CurrentEvent = prev
c.log.Debug().Stringer("event", prev).Msg("switched to prev event")
}

func (c *Controller) moveEventsForwardPushing() error {
Expand Down
4 changes: 2 additions & 2 deletions internal/control/cli/timesheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ func (command *TimesheetCommand) Execute(args []string) error {
styledCategories.Add(cat, style)
}

startDate, err := model.FromString(command.FromDay)
startDate, err := model.DateFromString(command.FromDay)
if err != nil {
log.Fatal().Msgf("from date '%s' invalid", command.FromDay)
}
currentDate := startDate
finalDate, err := model.FromString(command.TilDay)
finalDate, err := model.DateFromString(command.TilDay)
if err != nil {
log.Fatal().Msgf("til date '%s' invalid", command.TilDay)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/control/cli/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (command *TUICommand) Execute(_ []string) error {
if command.Day == "" {
initialDay = model.Date{Year: now.Year(), Month: int(now.Month()), Day: now.Day()}
} else {
initialDay, err = model.FromString(command.Day)
initialDay, err = model.DateFromString(command.Day)
if err != nil {
return fmt.Errorf("could not parse given date (%w)", err)
}
Expand Down
10 changes: 8 additions & 2 deletions internal/model/date_and_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ func (d Date) Valid() bool {
return true
}

// FromString creates a date from a string in the format "YYYY-MM-DD".
func FromString(s string) (Date, error) {
type DateSlice []Date

func (a DateSlice) Len() int { return len(a) }
func (a DateSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a DateSlice) Less(i, j int) bool { return a[i].IsBefore(a[j]) }

// DateFromString creates a date from a string in the format "YYYY-MM-DD".
func DateFromString(s string) (Date, error) {
var result Date
var err error

Expand Down
4 changes: 2 additions & 2 deletions internal/model/goal.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ func NewRangedGoalFromConfig(cfg []config.RangedGoal) (*RangedGoal, error) {
result := RangedGoal{}

for i := range cfg {
start, err := FromString(cfg[i].Start)
start, err := DateFromString(cfg[i].Start)
if err != nil {
return nil, fmt.Errorf("error parsing start date of range no. %d: %w", i, err)
}
end, err := FromString(cfg[i].End)
end, err := DateFromString(cfg[i].End)
if err != nil {
return nil, fmt.Errorf("error parsing start date of range no. %d: %w", i, err)
}
Expand Down
103 changes: 98 additions & 5 deletions internal/storage/providers/files_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package providers

import (
"fmt"
"os"
"regexp"
"sort"
"sync"
"time"

Expand All @@ -14,6 +17,8 @@ import (

const notSameDayEventErrorMsg = string("event does not start and end on the same day")

var fileDateNamingRegex = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)

// FilesDataProvider ...
type FilesDataProvider struct {
BasePath string
Expand Down Expand Up @@ -96,15 +101,74 @@ func (p *FilesDataProvider) RemoveEvents([]storage.EventIdentifier) error {
return nil
}

// TODO: doc GetEventAfter
func (p *FilesDataProvider) GetEventAfter(time.Time) (*model.Event, error) {
p.log.Fatal().Msg("TODO IMPL(GetEventAfter)")
// GetEventAfter retrieves the first event after the specified time.
func (p *FilesDataProvider) GetEventAfter(t time.Time) (*model.Event, error) {
p.log.Debug().Msgf("getting first event after %s", t.String())
defer p.log.Debug().Msgf("done getting first event after %s", t.String())

availableDates, err := p.getAvailableDates()
if err != nil {
return nil, fmt.Errorf("error getting available dates (%w)", err)
}
p.log.Trace().Msgf("have %d available dates", len(availableDates))

sort.Sort(model.DateSlice(availableDates))

dateForT := model.DateFromGotime(t)

for _, d := range availableDates {
if d.IsBefore(dateForT) {
p.log.Trace().Msgf("skipping date '%s' because it is before the target time", d.String())
continue
}
p.log.Trace().Msgf("getting file handler for date '%s'", d.String())
fh, err := p.getFileHandler(d)
if err != nil {
return nil, fmt.Errorf("error getting file handler for date '%s', which should not happen since the file should exist (%w)", d.String(), err)
}
for _, event := range fh.data.Events {
if event.Start == t || event.Start.After(t) {
p.log.Trace().Msgf("found event starting after target time: %s", event.String())
return event, nil
}
}
}
return nil, nil
}

// TODO: doc GetEventBefore
func (p *FilesDataProvider) GetEventBefore(time.Time) (*model.Event, error) {
p.log.Fatal().Msg("TODO IMPL(GetEventBefore)")
func (p *FilesDataProvider) GetEventBefore(t time.Time) (*model.Event, error) {
p.log.Debug().Msgf("getting last event before %s", t.String())
defer p.log.Debug().Msgf("done getting last event before %s", t.String())

availableDates, err := p.getAvailableDates()
if err != nil {
return nil, fmt.Errorf("error getting available dates (%w)", err)
}
p.log.Trace().Msgf("have %d available dates", len(availableDates))

sort.Sort(sort.Reverse(model.DateSlice(availableDates)))

dateForT := model.DateFromGotime(t)

for _, d := range availableDates {
if d.IsAfter(dateForT) {
p.log.Trace().Msgf("skipping date '%s' because it is after the target time", d.String())
continue
}
p.log.Trace().Msgf("getting file handler for date '%s'", d.String())
fh, err := p.getFileHandler(d)
if err != nil {
return nil, fmt.Errorf("error getting file handler for date '%s', which should not happen since the file should exist (%w)", d.String(), err)
}
for i := len(fh.data.Events) - 1; i >= 0; i-- {
event := fh.data.Events[i]
if event.End == t || event.End.Before(t) {
p.log.Trace().Msgf("found event ending before target time: %s", event.String())
return event, nil
}
}
}
return nil, nil
}

Expand Down Expand Up @@ -279,3 +343,32 @@ func eventStartsAndEndsOnSameDate(e *model.Event) bool {
func timesOnSameDate(a, b time.Time) bool {
return a.Year() != b.Year() || a.YearDay() != b.YearDay()
}

// NOTE: this function is fine, but its use could be improved, because we really should only need to call this once
func (p *FilesDataProvider) getAvailableDates() ([]model.Date, error) {
p.log.Debug().Msg("getting available dates")
defer p.log.Debug().Msg("done getting available dates")

files, err := os.ReadDir(p.BasePath)
if err != nil {
return nil, fmt.Errorf("error reading directory (%w)", err)
}
var dates []model.Date
for _, f := range files {
if f.IsDir() {
p.log.Trace().Msgf("skipping directory '%s'", f.Name())
continue
}
if !fileDateNamingRegex.MatchString(f.Name()) {
p.log.Trace().Msgf("skipping non-date file '%s'", f.Name())
continue
}
d, err := model.DateFromString(f.Name())
if err != nil {
return nil, fmt.Errorf("error parsing date from file name '%s' (%w)", f.Name(), err)
}
dates = append(dates, d)
}
return dates, nil

}

0 comments on commit 7e1ca65

Please sign in to comment.