planner

package
v0.0.0-...-ea5ad73 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 12, 2026 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Index

Constants

View Source
const (
	SolverTimeout = time.Second * 10

	PhotoApiBaseURL = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photo_reference=%s&key=%s"
)
View Source
const (
	NumPlansDefault                       = 5
	TopSolutionsCountDefault              = 5
	DefaultPlaceSearchRadius              = 20000 // default to 20km (~12.43 miles)
	MaxSolutionsToSaveCount               = 100
	CategorizedPlaceIterInitFailureErrMsg = "categorized places iterator init failure"
	ErrMsgMismatchIterAndPlace            = "mismatch in iterator status vector length"
	ErrMsgRepeatedPlaceInSameTrip         = "repeated places in the same trip"
)
View Source
const (
	ValidSolutionFound     = 200
	InvalidRequestLocation = 400
	NoValidSolution        = 404
	RequestTimeOut         = 408
	InternalError          = 500
)
View Source
const NumWorkers = 10
View Source
const (
	UseGeneratedImagesThreshold = 5
)

Variables

This section is empty.

Functions

func MapSlice

func MapSlice[T, V any](ts []T, fn func(t T) V) []V

MapSlice is a generic function for mapping a slice to another

Types

type Announcement

type Announcement struct {
	ID        string `json:"id"`
	AdminID   string `json:"admin_id"`
	Subject   string `json:"subject"`
	Message   string `json:"message"`
	Timestamp string `json:"timestamp"`
}

type AuthError

type AuthError struct {
	ErrType AuthErrorType
	ErrMsg  error
}

func (*AuthError) Error

func (e *AuthError) Error() string

func (*AuthError) GetErrorMessage

func (e *AuthError) GetErrorMessage() string

GetErrorMessage returns a user-friendly error message based on the auth type

func (*AuthError) IsCookieError

func (e *AuthError) IsCookieError() bool

IsCookieError returns true if the auth error is related to JWT cookie authentication

func (*AuthError) IsTokenError

func (e *AuthError) IsTokenError() bool

IsTokenError returns true if the auth error is related to PAT token authentication

type AuthErrorType

type AuthErrorType string
const (
	AuthErrorTypeToken  AuthErrorType = "token"
	AuthErrorTypeCookie AuthErrorType = "cookie"
)

type CityView

type CityView struct {
	City    string `json:"city"`
	Region  string `json:"region"`
	Country string `json:"country"`
}

type Dispatcher

type Dispatcher struct {
	JobQueue *PriorityJobQueue
	// contains filtered or unexported fields
}

func NewDispatcher

func NewDispatcher(s *Solver, c *iowrappers.RedisClient) *Dispatcher

func (*Dispatcher) Run

func (d *Dispatcher) Run(ctx context.Context)

func (*Dispatcher) Stop

func (d *Dispatcher) Stop()

func (*Dispatcher) Wait

func (d *Dispatcher) Wait()

type Environment

type Environment string
const (
	ProductionEnvironment  Environment = "production"
	StagingEnvironment     Environment = "staging"
	TestingEnvironment     Environment = "testing"
	DevelopmentEnvironment Environment = "development"
)

type GenericWorker

type GenericWorker struct {
	// contains filtered or unexported fields
}

GenericWorker is a flexible worker that can handle jobs using registered JobHandlers

func NewGenericWorker

func NewGenericWorker(idx int, jobQueue *PriorityJobQueue, wg *sync.WaitGroup) *GenericWorker

NewGenericWorker creates a new generic worker

func (*GenericWorker) RegisterHandler

func (w *GenericWorker) RegisterHandler(handler JobHandler)

RegisterHandler registers a job handler for a specific job type

func (*GenericWorker) Run

func (w *GenericWorker) Run(ctx context.Context)

Run starts the worker's processing loop

type GeocodeCityView

type GeocodeCityView struct {
	Count  int
	Cities map[string]string
}

type JobHandler

type JobHandler interface {
	// Execute processes a job and returns an error if execution fails
	Execute(ctx context.Context, job *iowrappers.Job) error
	// JobType returns the type of job this handler processes
	JobType() string
}

JobHandler interface allows different job types to be executed by workers Implementing this interface enables adding new job types without modifying worker code

type JobStore

type JobStore struct {
	// contains filtered or unexported fields
}

JobStore tracks and deduplicates concurrent job executions

type MinPriorityQueue

type MinPriorityQueue[T PriorityQueueItem] struct {
	// contains filtered or unexported fields
}

func (*MinPriorityQueue[T]) Len

func (pq *MinPriorityQueue[T]) Len() int

func (*MinPriorityQueue[T]) Less

func (pq *MinPriorityQueue[T]) Less(i, j int) bool

func (*MinPriorityQueue[T]) Pop

func (pq *MinPriorityQueue[T]) Pop() interface{}

func (*MinPriorityQueue[T]) Push

func (pq *MinPriorityQueue[T]) Push(item interface{})

func (*MinPriorityQueue[T]) Swap

func (pq *MinPriorityQueue[T]) Swap(i, j int)

type MultiDimIterator

type MultiDimIterator struct {
	Categories []POI.PlaceCategory
	Status     []int
	Stop       bool  // indicate iterator is stopped
	Size       []int // number of items in each category
}

func (*MultiDimIterator) ClearStatus

func (mdTagIter *MultiDimIterator) ClearStatus()

func (*MultiDimIterator) HasNext

func (mdTagIter *MultiDimIterator) HasNext() bool

func (*MultiDimIterator) Init

func (mdTagIter *MultiDimIterator) Init(categories []POI.PlaceCategory, placeClusters [][]matching.Place) error

func (*MultiDimIterator) Next

func (mdTagIter *MultiDimIterator) Next() bool

type MultiPlanningReq

type MultiPlanningReq struct {
	// contains filtered or unexported fields
}

MultiPlanningReq can be used to represent a multi-day planning request for a single location or a group of requests for different locations

type MyPlanner

type MyPlanner struct {
	RedisClient        *iowrappers.RedisClient
	RedisStreamName    string
	PhotoClient        iowrappers.PhotoClient
	Solver             Solver
	ResultHTMLTemplate *template.Template
	TripHTMLTemplate   *template.Template
	PlanningEvents     chan iowrappers.PlanningEvent
	Environment        Environment
	Configs            map[string]interface{}
	OAuth2Config       *oauth2.Config
	Mailer             *iowrappers.Mailer
	GeonamesApiKey     string
	MapsClientApiKey   string
	BlobBucket         string
	Dispatcher         *Dispatcher
}

func (*MyPlanner) Destroy

func (p *MyPlanner) Destroy()

func (*MyPlanner) GetPlaceDetails

func (p *MyPlanner) GetPlaceDetails(ctx *gin.Context)

func (*MyPlanner) Init

func (p *MyPlanner) Init(mapsClientApiKey string, redisURL *url.URL, redisStreamName string, configs map[string]interface{}, oauthClientID string, oauthClientSecret string, domain string, geonamesApiKey string, blobBucket string)

func (*MyPlanner) ListPATs

func (p *MyPlanner) ListPATs(ctx *gin.Context)

func (*MyPlanner) Planning

func (p *MyPlanner) Planning(ctx context.Context, planningRequest *PlanningRequest, user string) (resp PlanningResponse)

func (*MyPlanner) ProcessPlanningEvent

func (p *MyPlanner) ProcessPlanningEvent(worker int, wg *sync.WaitGroup)

func (*MyPlanner) RevokePAT

func (p *MyPlanner) RevokePAT(ctx *gin.Context)

func (*MyPlanner) SetPlanSavedStatusForUser

func (p *MyPlanner) SetPlanSavedStatusForUser(ctx *gin.Context, numResults int, resp PlanningResponse, uv user.View) error

func (*MyPlanner) SetupRouter

func (p *MyPlanner) SetupRouter(serverPort string) *http.Server

func (*MyPlanner) UrlMigrationHandler

func (p *MyPlanner) UrlMigrationHandler(ctx *gin.Context)

func (*MyPlanner) UserAuthentication

func (p *MyPlanner) UserAuthentication(ctx *gin.Context, minimumUserLevel user.Level) (user.View, *AuthError)

func (*MyPlanner) UserEmailVerify

func (p *MyPlanner) UserEmailVerify(ctx *gin.Context)

func (*MyPlanner) UserRatingsTotalMigrationHandler

func (p *MyPlanner) UserRatingsTotalMigrationHandler(ctx *gin.Context)

func (*MyPlanner) UserSignup

func (p *MyPlanner) UserSignup(ctx *gin.Context)

type NewTokenInfo

type NewTokenInfo struct {
	Name               string `json:"name"`
	ExpirationDuration string `json:"expiration_duration,omitempty"` // Optional: e.g., "24h", "7d", "30d"
}

type PATView

type PATView struct {
	Name      string `json:"name"`
	Id        string `json:"id"`
	ExpiresAt string `json:"expiresAt"`
}

type PlaceDetailsResp

type PlaceDetailsResp struct {
	ID               string
	Name             string
	URL              string
	FormattedAddress string
	PhotoURL         string
	Summary          string
}

type PlaceMatcher

type PlaceMatcher struct {
	// contains filtered or unexported fields
}

func NewPlaceMatcher

func NewPlaceMatcher() *PlaceMatcher

func (*PlaceMatcher) MatchPlaces

func (pm *PlaceMatcher) MatchPlaces(req *matching.FilterRequest, m matching.Matcher) ([]matching.Place, error)

MatchPlaces uses strategy pattern to match places at runtime

type PlacePlanningDetails

type PlacePlanningDetails struct {
	Name     string            `json:"name"`
	URL      string            `json:"url"`
	Category string            `json:"category"`
	TimeSlot matching.TimeSlot `json:"time_slot"`
}

type PlanningJobHandler

type PlanningJobHandler struct {
	// contains filtered or unexported fields
}

PlanningJobHandler implements JobHandler for planning solution jobs

func NewPlanningJobHandler

func NewPlanningJobHandler(solver *Solver, store *JobStore, redis *iowrappers.RedisClient) *PlanningJobHandler

NewPlanningJobHandler creates a new planning job handler

func (*PlanningJobHandler) Execute

func (h *PlanningJobHandler) Execute(ctx context.Context, job *iowrappers.Job) error

Execute processes a planning job

func (*PlanningJobHandler) JobType

func (h *PlanningJobHandler) JobType() string

JobType returns the job type identifier

type PlanningPostRequest

type PlanningPostRequest struct {
	Country   string      `json:"country"`
	City      string      `json:"city"`
	Weekday   POI.Weekday `json:"weekday"`
	StartTime POI.Hour    `json:"start_time"`
	EndTime   POI.Hour    `json:"end_time"`
	NumVisit  uint        `json:"num_visit"`
	NumEatery uint        `json:"num_eatery"`
}

type PlanningRequest

type PlanningRequest struct {
	Location         POI.Location  `json:"location"`
	Slots            []SlotRequest `json:"slots"`
	TravelDate       string
	NumPlans         int
	SearchRadius     uint           `json:"radius"`
	PriceLevel       POI.PriceLevel `json:"price_level"`
	PreciseLocation  bool
	WithNearbyCities bool
	// contains filtered or unexported fields
}

type PlanningResp

type PlanningResp struct {
	Solutions []PlanningSolution
	Err       error
	ErrorCode int
}

type PlanningResponse

type PlanningResponse struct {
	TravelDestination string       `json:"travel_destination"`
	TravelPlans       []TravelPlan `json:"travel_plans"`
	TripDetailsURL    []string     `json:"trip_details_url"`
	Err               error        `json:"error"`
	StatusCode        int          `json:"status_code"`
}

type PlanningSolution

type PlanningSolution struct {
	ID              string              `json:"id"`
	PlaceNames      []string            `json:"place_names"`
	PlaceIDS        []string            `json:"place_ids"`
	PlaceLocations  [][2]float64        `json:"place_locations"` // lat,lng
	PlaceAddresses  []string            `json:"place_addresses"`
	PlaceURLs       []string            `json:"place_urls"`
	PlaceCategories []POI.PlaceCategory `json:"place_categories"`
	Score           float64             `json:"score"`
	ScoreOld        float64             `json:"score_old"`
	PlanSpec        string              `json:"plan_spec"`
}

func (PlanningSolution) Key

func (ps PlanningSolution) Key() float64

type PriorityJobQueue

type PriorityJobQueue struct {
	// contains filtered or unexported fields
}

PriorityJobQueue manages jobs across three priority levels Workers always pull from high-priority queue first, then normal, then low

func NewPriorityJobQueue

func NewPriorityJobQueue(bufferSize int) *PriorityJobQueue

NewPriorityJobQueue creates a new priority-based job queue bufferSize determines the capacity of each priority channel

func (*PriorityJobQueue) Close

func (pq *PriorityJobQueue) Close()

Close closes all priority queues

func (*PriorityJobQueue) Dequeue

func (pq *PriorityJobQueue) Dequeue() *iowrappers.Job

Dequeue retrieves the next job, prioritizing high > normal > low Returns nil when all queues are closed and empty

func (*PriorityJobQueue) Enqueue

func (pq *PriorityJobQueue) Enqueue(job *iowrappers.Job) bool

Enqueue adds a job to the appropriate priority queue

func (*PriorityJobQueue) Len

func (pq *PriorityJobQueue) Len() int

Len returns the approximate total number of jobs across all priorities Note: This is a snapshot and may not be accurate in concurrent scenarios

func (*PriorityJobQueue) Stats

func (pq *PriorityJobQueue) Stats() (high, normal, low int)

Stats returns the number of jobs in each priority queue

type PriorityQueueItem

type PriorityQueueItem interface {
	Key() float64
}

type ProfileView

type ProfileView struct {
	Username    string
	TravelPlans []user.TravelPlanView
}

type RequestIdKey

type RequestIdKey string

type RevokeTokenInfo

type RevokeTokenInfo struct {
	Name string `json:"name"`
}

type SlotRequest

type SlotRequest struct {
	Weekday  POI.Weekday       `json:"weekday"`
	TimeSlot matching.TimeSlot `json:"time_slot"`
	Category POI.PlaceCategory `json:"category"`
}

SlotRequest represents the properties of each row in the tabular travel plan, although not all of these are displayed to users

type Solver

type Solver struct {
	Searcher *iowrappers.PoiSearcher
	// contains filtered or unexported fields
}

func (*Solver) FindBestPlanningSolutions

func (s *Solver) FindBestPlanningSolutions(ctx context.Context, placeClusters [][]matching.Place, maxSolutionsToSaveCount int, iterator *MultiDimIterator, radius uint, spec string) (resp *PlanningResp)

func (*Solver) FindOptimalPlan

func (s *Solver) FindOptimalPlan(placeClusters [][]matching.Place) ([]string, error)

func (*Solver) Init

func (s *Solver) Init(poiSearcher *iowrappers.PoiSearcher, placeDedupeCountLimit int, nearbyCitiesCountLimit int)

func (*Solver) Solve

func (s *Solver) Solve(ctx context.Context, req *PlanningRequest) *PlanningResp

func (*Solver) SolveHungarianOptimal

func (s *Solver) SolveHungarianOptimal(ctx context.Context, req *PlanningRequest) ([]PlacePlanningDetails, error)

func (*Solver) SolveWithNearbyCities

func (s *Solver) SolveWithNearbyCities(ctx context.Context, req *MultiPlanningReq) *PlanningResp

func (*Solver) ValidateLocation

func (s *Solver) ValidateLocation(ctx context.Context, location *POI.Location) bool

type TimeSectionPlace

type TimeSectionPlace struct {
	ID        string            `json:"id"`
	PlaceName string            `json:"place_name"`
	Category  POI.PlaceCategory `json:"category"`
	StartTime POI.Hour          `json:"start_time"`
	EndTime   POI.Hour          `json:"end_time"`
	Address   string            `json:"address"`
	URL       string            `json:"url"`
	PlaceIcon string            `json:"place_icon_css_class"`
}

type TravelPlan

type TravelPlan struct {
	ID           string             `json:"id"`
	Places       []TimeSectionPlace `json:"places"`
	Saved        bool               `json:"saved"`
	PlanningSpec string             `json:"planning_spec"`
}

type TripDetailResp

type TripDetailResp struct {
	OriginalPlanID    string
	LatLongs          [][2]float64
	PlaceCategories   []POI.PlaceCategory
	PlaceDetails      []PlaceDetailsResp
	ShownActive       []bool
	TravelDestination string
	TravelDate        string
	Score             float64
	ScoreOld          float64
	ApiKey            string
}

TODO: deprecate Score and ScoreOld fields

type UserLoginResponse

type UserLoginResponse struct {
	Email    string `json:"email"`
	Username string `json:"username"`
	Jwt      string `json:"jwt"`
	Status   string `json:"status"`
}

type Vertex

type Vertex struct {
	K      float64 // key or priority
	Name   string  // vertex name
	Object interface{}
}

func (Vertex) Key

func (v Vertex) Key() float64

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL