lesson

package
v0.0.0-...-fc927da Latest Latest
Warning

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

Go to latest
Published: Jan 19, 2021 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var StringOps = map[Op]bool{OpContains: true, OpStartsWith: true}

Functions

func ValidateCtxEval

func ValidateCtxEval(str string) error

Types

type Act

type Act struct {
	Scenes      Scenes            `json:"scenes"`                //This should get gorm embedded as json
	RepeatUntil *RepeatUntilLogic `json:"repeatLogic,omitempty"` //This should get gorm embedded as json
}

func NewAct

func NewAct(owner account.User, group account.Group) *Act

func (*Act) CouldFinishAct

func (a *Act) CouldFinishAct(ctx *Model) (bool, error)

type Acts

type Acts []Act

func (Acts) Scan

func (acts Acts) Scan(src interface{}) error

func (Acts) Value

func (acts Acts) Value() (driver.Value, error)

type BoolComparable

type BoolComparable struct {
	Impl bool
}

func (BoolComparable) EqualTo

func (lhs BoolComparable) EqualTo(rhs Comparable) bool

func (BoolComparable) LessThan

func (lhs BoolComparable) LessThan(rhs Comparable) bool

type ClientID

type ClientID uuid.UUID

TODO this to probably become ClientID!

type Comparable

type Comparable interface {
	EqualTo(Comparable) bool
	LessThan(Comparable) bool
}

func MakeComparable

func MakeComparable(str string) Comparable

type ConsoleMessage

type ConsoleMessage struct {
	WebsocketMessage
	Type             ConsoleMessageType     `json:"type"`
	ClientOutChannel *chan<- ConsoleMessage //Optional - the output chanel associated with this console message for this client
}

func (ConsoleMessage) MakeEventLoopEvent

func (msg ConsoleMessage) MakeEventLoopEvent() (EventLoopEvent, error)

type ConsoleMessageType

type ConsoleMessageType string
const (
	ConsoleSkipMessage         ConsoleMessageType = "skip_round"
	ConsoleRegisterMessage     ConsoleMessageType = "register"
	ConsoleRequestInputMessage ConsoleMessageType = "request_input"
	ConsoleShowLoadingMessage  ConsoleMessageType = "show_loading"
	ConsoleShowIdle            ConsoleMessageType = "show_idle"
	ConsoleEndGameMessage      ConsoleMessageType = "end_game"
)

type Controller

type Controller struct {
	Planner Planner
	Model   Model
	//Channels...
	// Channels for communicating with the event loop
	EventChannelIn <-chan ControllerEvent
	EventLoopOut   chan<- EventLoopEvent
}

func (Controller) Run

func (c Controller) Run() error

Note: we assume that LoadPlan has already happened

type ControllerEvent

type ControllerEvent interface {
	Handle(*Controller) error
}

type CtrlAddPlayerEvent

type CtrlAddPlayerEvent struct {
	PlayerToken ClientID
	PlayerName  string
}

func (CtrlAddPlayerEvent) Handle

func (event CtrlAddPlayerEvent) Handle(ctrl *Controller) error

type CtrlMakeTeamsEvent

type CtrlMakeTeamsEvent struct{}

func (CtrlMakeTeamsEvent) Handle

func (event CtrlMakeTeamsEvent) Handle(ctrl *Controller) error

type EndRegistrationEvent

type EndRegistrationEvent struct {
}

type EndSceneEvent

type EndSceneEvent struct {
	NextRound RoundIdx
}

type EventLoop

type EventLoop struct {
	CurrentRound        RoundIdx //Current round index as we understand it. A 0, 0, 0 round = registration
	InactivityTimeout   time.Duration
	LeaderPlayerToken   ClientID
	RegistrationTimeout time.Duration
	// Channels for sending and receiving messages to/from theatre websockets.
	// Go routines handling websockets must convert their respective events to EventLoopEvents
	TheatreChannelIn   <-chan EventLoopEvent
	TheatreChannelOuts []chan<- EventLoopEvent
	// Channels for sending and receiving messages to/from console websockets
	// Go routines handling websockets must convert their respective events to EventLoopEvents
	ConsoleChannelIn   <-chan EventLoopEvent
	ConsoleChannelOuts map[ClientID]chan<- EventLoopEvent
	// Channels for telling the controller about state changes
	ControllerChannelOut chan<- ControllerEvent
	EventLoopChannelIn   <-chan EventLoopEvent
}

TODO lesson needs to construct, and make the channels.

func (*EventLoop) AfterHandleConsoleEvent

func (loop *EventLoop) AfterHandleConsoleEvent(msg ConsoleMessageIn) error

func (*EventLoop) AfterHandleScreenEvent

func (loop *EventLoop) AfterHandleScreenEvent(event ShowScreenEvent) error

func (*EventLoop) AfterHandleTheatreEvent

func (loop *EventLoop) AfterHandleTheatreEvent(msg TheatreMessageIn) error

func (*EventLoop) AfterLessonEnd

func (loop *EventLoop) AfterLessonEnd() error

func (*EventLoop) AfterLessonStart

func (loop *EventLoop) AfterLessonStart() error

func (*EventLoop) AfterRegistration

func (loop *EventLoop) AfterRegistration() error

func (*EventLoop) BeforeHandleConsoleEvent

func (loop *EventLoop) BeforeHandleConsoleEvent(msg ConsoleMessageIn) error

func (*EventLoop) BeforeHandleScreenEvent

func (loop *EventLoop) BeforeHandleScreenEvent(event ShowScreenEvent) error

func (*EventLoop) BeforeHandleTheatreEvent

func (loop *EventLoop) BeforeHandleTheatreEvent(msg TheatreMessageIn) error

func (*EventLoop) BeforeLessonEnd

func (loop *EventLoop) BeforeLessonEnd() error

func (*EventLoop) BeforeLessonStart

func (loop *EventLoop) BeforeLessonStart() error

func (*EventLoop) BeforeRegistration

func (loop *EventLoop) BeforeRegistration() error

func (*EventLoop) DoRegistration

func (loop *EventLoop) DoRegistration() error

func (*EventLoop) HandleConsoleEvent

func (loop *EventLoop) HandleConsoleEvent(msg ConsoleMessageIn) error

func (*EventLoop) HandleEvents

func (loop *EventLoop) HandleEvents() error

func (*EventLoop) HandleScreenEvent

func (loop *EventLoop) HandleScreenEvent(event ShowScreenEvent) error

TODO this probably requires returning a new round (or something similar) so we can trigger the "new" round type stuff TODO - for now let's keep it simple...

func (*EventLoop) HandleTheatreEvent

func (loop *EventLoop) HandleTheatreEvent(msg TheatreMessageIn) error

func (*EventLoop) LessonEnd

func (loop *EventLoop) LessonEnd() error

func (*EventLoop) LessonStart

func (loop *EventLoop) LessonStart() error

func (*EventLoop) Run

func (loop *EventLoop) Run() error

type EventLoopEvent

type EventLoopEvent interface {
	HandleFromController(*EventLoop) error
	HandleFromConsole(*EventLoop, ClientID) error
	HandleFromTheatre(*EventLoopEvent) error
}

type EventLoopTransformable

type EventLoopTransformable interface {
	MakeEventLoopEvent() EventLoopEvent
}

type FloatComparable

type FloatComparable struct {
	Impl float64
}

func (FloatComparable) EqualTo

func (lhs FloatComparable) EqualTo(rhs Comparable) bool

func (FloatComparable) LessThan

func (lhs FloatComparable) LessThan(rhs Comparable) bool

type InputResponse

type InputResponse struct {
	PlayerToken ClientID
}

type JumpToSceneRequest

type JumpToSceneRequest struct {
	TargetRound RoundIdx
}

type Layout

type Layout string
const (
	LayoutTitle              Layout = "title"
	LayoutSectionTitle       Layout = "section title"
	LayoutTitleContent       Layout = "content with title (optional)"
	LayoutHeroTop            Layout = "hero"
	LayoutTwoColumnContent   Layout = "two column content"
	LayoutThreeColumnContent Layout = "three column content"
	LayoutQuadrants          Layout = "four quadrants content"
	LayoutContentAndImage           = "content with image"
)

type LayoutFlag

type LayoutFlag string
const (
	FillPage              LayoutFlag = "fill page"
	VariationTop          LayoutFlag = "top variation"
	VariationMiddle       LayoutFlag = "middle variation"
	VariationBottom       LayoutFlag = "bottom variation"
	VariationLeft         LayoutFlag = "left variation"
	VariationRight        LayoutFlag = "right variation"
	VariationImageStretch LayoutFlag = "image stretch"
	VariationImageCover   LayoutFlag = "image cover"
	VariationImageContain LayoutFlag = "image contain"
	VariationImageRepeat  LayoutFlag = "image repeat"
)

type LayoutFlagsStruct

type LayoutFlagsStruct map[LayoutFlag]bool

type Lesson

type Lesson struct {
	EventLoop  EventLoop
	Model      Model
	Register   Register
	Controller Controller
}

func NewLesson

func NewLesson(register Register, inactivityTimeout time.Duration) *Lesson

TODO rework this

func (*Lesson) Close

func (lesson *Lesson) Close() error

func (*Lesson) Run

func (lesson *Lesson) Run(planId uuid.UUID) error

type Model

type Model struct {
	Round     Round
	AllRounds map[RoundIdx]Round
	Players   []Player
	Teams     []Team
	Scores    Scores
}

func NewLessonModel

func NewLessonModel() *Model

func (*Model) Eval

func (ctx *Model) Eval(str string) (string, error)

type Op

type Op string
const (
	OpEquals            Op = "="
	OpNotEquals         Op = "!="
	OpLessThan          Op = "<"
	OpLessThanEquals    Op = "<="
	OpGreaterThan       Op = ">"
	OpGreaterThanEquals Op = ">="
	OpContains          Op = "contains"
	OpStartsWith        Op = "starts-with"
)

func (Op) Eval

func (op Op) Eval(lhsStr string, rhsStr string) bool

type Plan

type Plan struct {
	account.UserObject
	Name           string
	Description    string
	TeamStructures TeamStructures //This is embedded
	Acts           []Act          // This is also embedded.
}

type Planner

type Planner struct {
	PlanID uuid.UUID
	Plan   *Plan

	//A channel for making some of the fetch from the db async
	QuestionRetrievalChannel chan<- QuestionDraw
	// contains filtered or unexported fields
}

func NewPlanner

func NewPlanner(planID uuid.UUID, conn *gorm.DB) *Planner

func (*Planner) FetchQuestions

func (planner *Planner) FetchQuestions(rqf ResolvedQuestionFilter) error

TODO change this - should be for a question filter - a resolved one at that probably TODO need to write a simple routine for running a question filter

func (*Planner) LoadPlan

func (planner *Planner) LoadPlan() error

func (*Planner) PlanIsLoaded

func (planner *Planner) PlanIsLoaded() bool

type Player

type Player struct {
	Name          string
	Avatar        []byte
	Token         ClientID //Generated unique to securely disambiguate players //TODO - align with JWT //TODO (probably in the handler itself)
	ScoreCard     ScoreCard
	AllScoreCards map[RoundIdx]ScoreCard
	Responses     map[RoundIdx]Response //Links the players response to the question for them generated at that round
}

A stripped-down version of user that can be baked into templates - not even an email address!

type PlayerRanking

type PlayerRanking struct {
	ScoreName ScoreName
	Player    Player
	Score     float64
}

type PlayerRankings

type PlayerRankings []PlayerRanking

type PlayerResponseEvent

type PlayerResponseEvent struct {
	PlayerToken  uuid.UUID
	QuestionLink QuestionLink
	Data         interface{}
}

type PlayerScores

type PlayerScores map[ScoreName]PlayerRankings

type PreloadScreen

type PreloadScreen struct {
	Round RoundIdx
	View  RenderedView
}

type Question

type Question struct {
	account.UserObject
	Content string         `json:"content"`
	Header  string         `json:"header,omitempty"`
	Image   uuid.UUID      `json:"image,omitempty"`
	ByLine  string         `json:"byline,omitempty"`
	Tags    pq.StringArray `json:"tags" gorm:"type:varchar(64)[]"`
	Rules   QuestionRules  `json:"rules"`
}

func (*Question) Resolve

func (q *Question) Resolve(mdl *Model, _ *gorm.DB) (*ResolvedQuestion, error)

type QuestionContentType

type QuestionContentType string
const (
	ContentTypeText    QuestionContentType = "text"
	ContentTypeCanvas  QuestionContentType = "canvas"
	ContentTypeImage   QuestionContentType = "image"
	ContentTypeVideo   QuestionContentType = "gif"
	ContentTypeNumber  QuestionContentType = "number"
	ContentTypeEmoji   QuestionContentType = "emoji"
	ContentTypeBoolean QuestionContentType = "boolean"
	ContentTypeMathjax QuestionContentType = "mathjax"
	ContentTypeAudio   QuestionContentType = "audio"
	ContentTypeChart   QuestionContentType = "chart"
)

type QuestionDraw

type QuestionDraw map[RoundIdx]map[ClientID]Question

type QuestionFilterLiteral

type QuestionFilterLiteral struct {
	Field string //TODO need to json this.
	Op    Op
	Value string
}

func (QuestionFilterLiteral) ToString

func (qfl QuestionFilterLiteral) ToString() string

type QuestionFilterQuery

type QuestionFilterQuery struct {
	QuestionBanks []string
	CNF           [][]QuestionFilterLiteral
}

type QuestionIDList

type QuestionIDList []uuid.UUID

type QuestionLogic

type QuestionLogic string
const (
	SingleInput              QuestionLogic = "Single Input"
	SingleInputOpen          QuestionLogic = "Single Input No Right Answer"
	MultipleInput            QuestionLogic = "Multiple Input"
	MultipleInputOpen        QuestionLogic = "Multiple Input No Right Answer"
	SingleMultipleChoice     QuestionLogic = "Single Answer Multiple Choice"
	SingleMultipleChoiceOpen QuestionLogic = "Single Answer Multiple Choice No Right Answer"
	MultiMultipleChoice      QuestionLogic = "Multiple Answer Multiple Choice"
	MultiMultipleChoiceOpen  QuestionLogic = "Multiple Answer Multiple Choice No Right Answer"
	MultiBestMultipleChoice  QuestionLogic = "Multiple Answer Multiple Choice With Best Answer"
	ApproximateSingleInput   QuestionLogic = "Approximate Single Input"
	ApproximateMultipleInput QuestionLogic = "Approximate Multiple Inputs"
	VoteFPTP                 QuestionLogic = "Vote (First Past The Post)"
	VoteSTV                  QuestionLogic = "Vote (Single Transferable Vote)"
	VotePR                   QuestionLogic = "Vote (Proportional Scoring)"
	VoteAV                   QuestionLogic = "Vote (Alternative Vote)"
)

type QuestionRules

type QuestionRules struct {
	Logic        QuestionLogic       `json:"logic,omitempty"`
	ContentType  QuestionContentType `json:"contentType,omitempty"`
	Data         datatypes.JSON      `json:"data,omitempty"` //Extra data - e.g. best answer plus multiple choices
	ScoringRules ScoringRules        `json:"scoringRules,omitempty"`
}

func (*QuestionRules) Scan

func (qr *QuestionRules) Scan(src interface{}) error

func (*QuestionRules) Value

func (qr *QuestionRules) Value() (driver.Value, error)

type QuestionSet

type QuestionSet struct {
	QuestionIds []uuid.UUID         `json:"questionIds,omitempty"` //Either-or
	Query       QuestionFilterQuery `json:"query,omitempty"`       //Either-or
}

func (QuestionSet) Resolve

func (qf QuestionSet) Resolve(model *Model, conn *gorm.DB, usedQuestionSet map[uuid.UUID]bool)

usedQuesIds is to ensure we don't have any repeats

type Register

type Register struct {
	Timeout      time.Duration
	Done         bool
	RequireLogin bool
	OptDomain    *string
	LessonCode   string
}

type RegisterEvent

type RegisterEvent struct {
	PlayerToken ClientID
	OutChannel  chan<- EventLoopEvent
}

type RenderedView

type RenderedView struct {
	View
}

type RepeatCondition

type RepeatCondition struct {
	Op  Op     `json:"op"`
	LHS string `json:"lhs"` //Templates to be resolved
	RHS string `json:"rhs"`
}

func (RepeatCondition) Eval

func (rc RepeatCondition) Eval(ctx *Model) (bool, error)

type RepeatUntilLogic

type RepeatUntilLogic struct {
	Fixed uint16              `json:"fixed,omitempty"` //65k repeats is enough :)
	CNF   [][]RepeatCondition `json:"cnf,omitempty"`   //Takes precedence if it exists. CNF.
}

func (RepeatUntilLogic) Eval

func (rul RepeatUntilLogic) Eval(ctx *Model) (bool, error)

Eval returns true if we should break out and continue (i.e. no longer repeat) - the repeat condition is satisfied

func (*RepeatUntilLogic) Scan

func (rul *RepeatUntilLogic) Scan(src interface{}) error

func (*RepeatUntilLogic) Value

func (rul *RepeatUntilLogic) Value() (driver.Value, error)

type RequestForInput

type RequestForInput struct {
	InputRequest map[ClientID]ConsoleMessage //TODO ideally this is more processed than that before it hits the controller...
}

type Resolvable

type Resolvable interface {
	IsTemplated() bool
	Resolve(*Round, *gorm.DB) *Question
}

type ResolvedQuestion

type ResolvedQuestion struct {
	Question
}

type ResolvedQuestionFilter

type ResolvedQuestionFilter struct {
	QuestionSet
}

func (ResolvedQuestionFilter) ToWhereClause

func (rqf ResolvedQuestionFilter) ToWhereClause(tx *gorm.DB) (*gorm.DB, error)

type Response

type Response struct {
	Question *Question
	//TODO! this is gonna need to be pretty generic - make this work some'ow ;) Would be
	Data interface{}
}

type Round

type Round struct {
	RoundIdx
	Stats           RoundStats
	PreviousRound   *Round
	LinkedQuestions QuestionDraw
}

func FirstRound

func FirstRound() *Round

func (*Round) LinkedQuestion

func (round *Round) LinkedQuestion() *Question

Get the linked question if there is one, or a random linked question if there are many

func (*Round) NextAct

func (round *Round) NextAct(nextLinkedQuestions QuestionDraw) *Round

func (*Round) NextScene

func (round *Round) NextScene(nextLinkedQuestions QuestionDraw) *Round

func (*Round) RepeatAct

func (round *Round) RepeatAct(nextLinkedQuestions QuestionDraw) *Round

type RoundIdx

type RoundIdx struct {
	ActNumber   uint // Number left-to-right - almost like an Act
	SceneNumber uint // Going down which number
	Repetition  uint // A round may have multiple repetitions of that scene
}

type RoundStats

type RoundStats struct {
	QuestionNumber uint // total number of scenes that were also questions
	StartTime      time.Time
	Time           time.Time // the actual time
}

func NewRoundStats

func NewRoundStats() RoundStats

func (*RoundStats) PlayTime

func (rs *RoundStats) PlayTime() time.Duration

func (RoundStats) RefreshRoundStats

func (rs RoundStats) RefreshRoundStats() RoundStats

type Scene

type Scene struct {
	Template          View         `json:"template"`
	OptQuestionFilter *QuestionSet `json:"questionFilter,omitempty"`
}

type Scenes

type Scenes []Scene

func (*Scenes) Scan

func (scs *Scenes) Scan(src interface{}) error

func (*Scenes) Value

func (scs *Scenes) Value() (driver.Value, error)

type ScoreCard

type ScoreCard map[ScoreName]float64

type ScoreName

type ScoreName string
const DefaultScoreName ScoreName = "default"

type Scores

type Scores struct {
	PlayerScores PlayerScores
	TeamScores   TeamScores
}

func NewScores

func NewScores() Scores

type ScoringLogic

type ScoringLogic string
const (
	Simple           ScoringLogic = "simple"
	AddScores        ScoringLogic = "add-scores" //Adds two scores together
	InverseFrequency ScoringLogic = "inverse-frequency"
	SimpleByTeam     ScoringLogic = "simple-by-team"
	AddScoresByTeam  ScoringLogic = "add-by-team"
)

type ScoringRules

type ScoringRules map[ScoreName]ScoringLogic

type StringComparable

type StringComparable struct {
	Impl string
}

func (StringComparable) EqualTo

func (lhs StringComparable) EqualTo(rhs Comparable) bool

func (StringComparable) LessThan

func (lhs StringComparable) LessThan(rhs Comparable) bool

type Team

type Team struct {
	Name          string
	Avatar        []byte
	Players       []Player
	ScoreCard     ScoreCard
	AllScoreCards map[RoundIdx]ScoreCard
}

type TeamLogic

type TeamLogic string
const (
	TeamLogicBySize     TeamLogic = "By Size"
	TeamLogicByNumberOf TeamLogic = "By Number Of"
)

type TeamRanking

type TeamRanking struct {
	ScoreName
	Team  Team
	Score float64
}

type TeamRankings

type TeamRankings []TeamRanking

type TeamScores

type TeamScores map[ScoreName]TeamRankings

type TeamStructure

type TeamStructure struct {
	TeamLogic TeamLogic `json:"logic"`
	Count     uint8     `json:"count"`
}

type TeamStructures

type TeamStructures map[string]TeamLogic

func (*TeamStructures) Scan

func (tsrcts *TeamStructures) Scan(src interface{}) error

func (*TeamStructures) Value

func (tsrcts *TeamStructures) Value() (driver.Value, error)

type TheatreMessage

type TheatreMessage struct {
	WebsocketMessage
	Type TheatreMessageType `json:"type"`
}

func (TheatreMessage) MakeEventLoopEvent

func (msg TheatreMessage) MakeEventLoopEvent() (EventLoopEvent, error)

type TheatreMessageType

type TheatreMessageType string
const (
	TheatreSkipMessage         TheatreMessageType = "skip_round"
	TheatreUpdateScreenMessage TheatreMessageType = "update_screen" //Render a screen
	TheatreGoToRoundMessage    TheatreMessageType = "go_to_round"
	TheatreEndGameMessage      TheatreMessageType = "end_game"
)

type View

type View struct {
	Layout      Layout            `json:"layout"`
	LayoutFlags LayoutFlagsStruct `json:"layoutFlags,omitempty"`
	Title       string            `json:"title"`
	Byline      string            `json:"byline,omitempty"`
	Header      string            `json:"header,omitempty"`
	Footer      string            `json:"footer,omitempty"`
	Image       uuid.UUID         `json:"image,omitempty"`
	Caption     string            `json:"caption,omitempty"`
	Content     []string          `json:"content,omitempty"`
	Gallery     []uuid.UUID       `json:"gallery,omitempty"`
	Classes     []string          `json:"classes,omitempty"` //This is our theming hook
}

func (View) Render

func (view View) Render(mdl *Model) (*RenderedView, error)

type WebsocketMessage

type WebsocketMessage struct {
	PlayerToken ClientID        `json:"playerId,omitempty"`
	Round       RoundIdx        `json:"round,omitempty"` //Helps idempotency and avoids races
	Data        json.RawMessage `json:"data,omitempty"`
}

Jump to

Keyboard shortcuts

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