models

package
v0.0.0-...-a4f4c6b Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2023 License: GPL-3.0 Imports: 40 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AddGameToMonday = api.NewBinding[monday.ItemId, string](
	func(b api.Binding[monday.ItemId, string], args ...any) api.Request {
		game := args[0].(*Game)
		itemName := game.Website.String
		if game.Name.IsValid() {
			itemName = game.Name.String
		}
		mapping := args[1].(monday.Config).MondayMappingForModel(Game{})
		columnValues, err := mapping.ColumnValues(game)

		for columnID, columnValue := range args[2].(map[string]any) {
			columnValues[columnID] = columnValue
		}

		if err != nil {
			panic(err)
		}
		return monday.AddItem.Request(
			mapping.MappingBoardIDs()[0],
			mapping.MappingGroupIDs()[0],
			itemName,
			columnValues,
		)
	},
	monday.ResponseWrapper[monday.ItemId, string],
	monday.ResponseUnwrapped[monday.ItemId, string],
	monday.AddItem.GetResponseMethod(), func(binding api.Binding[monday.ItemId, string]) []api.BindingParam {
		return api.Params(
			"game", &Game{}, true,
			"config", reflect.TypeOf((*monday.Config)(nil)), true,
			"additionalColumnValues", map[string]any{},
		)
	}, false,
	func(client api.Client) (string, any) { return "jsonResponseKey", "create_item" },
	func(client api.Client) (string, any) { return "config", client.(*monday.Client).Config },
).SetName("AddGameToMonday")

AddGameToMonday adds a Game to the mapped board and group by constructing column values using the monday.MappingConfig.ColumnValues method for the monday.MappingConfig for Game. Arguments provided to Execute:

• game (*Game): The Game to add to the mapped board and group combination.

• config (monday.Config): The monday.Config used to fetch the monday.MappingConfig for Game from. This monday.MappingConfig is then used to generate the column values that are posted to the Monday API to construct a new item on the mapped board and group combination.

• additionalColumnValues (map[string]any): Any additional column values to create the monday.Item for the Game with. Column IDs that already exist in the column values generated for the Game will be overwritten by additionalColumnValues.

Execute returns the item ID of the newly created item. This can then be used to set the Game.Watched field appropriately if necessary.

View Source
var AddSteamAppToMonday = api.NewBinding[monday.ItemId, string](
	func(b api.Binding[monday.ItemId, string], args ...any) api.Request {
		app := args[0].(*SteamApp)
		itemName := app.Name
		mapping := args[1].(monday.Config).MondayMappingForModel(SteamApp{})
		columnValues, err := mapping.ColumnValues(app)
		if err != nil {
			panic(err)
		}
		return monday.AddItem.Request(
			mapping.MappingBoardIDs()[0],
			mapping.MappingGroupIDs()[0],
			itemName,
			columnValues,
		)
	},
	monday.ResponseWrapper[monday.ItemId, string],
	monday.ResponseUnwrapped[monday.ItemId, string],
	monday.AddItem.GetResponseMethod(),
	func(binding api.Binding[monday.ItemId, string]) []api.BindingParam {
		return api.Params(
			"game", &SteamApp{}, true,
			"config", reflect.TypeOf((*monday.Config)(nil)), true,
		)
	}, false,
	func(client api.Client) (string, any) { return "jsonResponseKey", "create_item" },
	func(client api.Client) (string, any) { return "config", client.(*monday.Client).Config },
).SetName("AddSteamAppToMonday")

AddSteamAppToMonday adds a SteamApp to the mapped board and group by constructing column values using the monday.MappingConfig.ColumnValues method for the monday.MappingConfig for SteamApp. Arguments provided to Execute:

• game (*SteamApp): The SteamApp to add to the mapped board and group combination.

• config (monday.Config): The monday.Config used to fetch the monday.MappingConfig for SteamApp from. This monday.MappingConfig is then used to generate the column values that are posted to the Monday API to construct a new item on the mapped board and group combination.

Execute returns the item ID of the newly created item. This can then be used to set the SteamApp.Watched field appropriately if necessary.

View Source
var GetGamesFromMonday = api.NewBinding[monday.ItemResponse, []*Game](
	func(b api.Binding[monday.ItemResponse, []*Game], args ...any) api.Request {
		page := args[0].(int)
		mapping := args[1].(monday.Config).MondayMappingForModel(Game{})
		boardIds := mapping.MappingBoardIDs()
		groupIds := mapping.MappingGroupIDs()
		return monday.GetItems.Request(page, boardIds, groupIds)
	},
	monday.ResponseWrapper[monday.ItemResponse, []*Game],
	monday.ResponseUnwrapped[monday.ItemResponse, []*Game],
	func(b api.Binding[monday.ItemResponse, []*Game], response monday.ItemResponse, args ...any) []*Game {
		items := monday.GetItems.Response(response)
		mapping := args[1].(monday.Config).MondayMappingForModel(Game{})
		db := args[2].(*gorm.DB)
		games := make([]*Game, 0)
		for _, item := range items {
			columnMap := make(map[string]monday.ColumnValue)
			for _, column := range item.ColumnValues {
				columnMap[column.Id] = column
			}

			var (
				id     uuid.UUID
				column monday.ColumnValue
				err    error
				ok     bool
			)
			if column, ok = columnMap[mapping.MappingModelInstanceIDColumnID()]; !ok {
				continue
			}

			game := Game{}
			if id, err = uuid.Parse(strings.Trim(column.Value, `"`)); err != nil {
				continue
			}

			if err := db.Find(&game, "id = ?", id).Error; errors.Is(err, gorm.ErrRecordNotFound) {
				continue
			}

			games = append(games, &game)

			var itemID int64
			if itemID, err = strconv.ParseInt(item.Id, 10, 64); err == nil {
				game.MondayItemID = int(itemID)
			}
			game.MondayBoardID = item.BoardId

			if column, ok = columnMap[mapping.MappingModelInstanceWatchedColumnID()]; ok {
				if column.Value != "null" {
					var watches monday.Votes
					if err = json.Unmarshal([]byte(column.Value), &watches); err == nil {
						if len(watches.VoterIds) > 0 {
							game.Watched = &item.Id
						}
					}
				} else {
					game.Watched = nil
				}
			}

			voteValues := []int{0, 0}
			for i, votesColumnID := range []string{
				mapping.MappingModelInstanceUpvotesColumnID(),
				mapping.MappingModelInstanceDownvotesColumnID(),
			} {
				var voteColumn monday.ColumnValue
				if voteColumn, ok = columnMap[votesColumnID]; !ok {
					continue
				}
				if voteColumn.Value == "null" {
					continue
				}
				var votes monday.Votes
				if err = json.Unmarshal([]byte(voteColumn.Value), &votes); err != nil {
					continue
				}
				voteValues[i] = len(votes.VoterIds)
			}
			game.Votes = int32(voteValues[0] - voteValues[1])
		}
		return games
	}, func(binding api.Binding[monday.ItemResponse, []*Game]) []api.BindingParam {
		return api.Params(
			"page", 0, true,
			"config", reflect.TypeOf((*monday.Config)(nil)), true,
			"db", &gorm.DB{}, true,
		)
	}, true,
	func(client api.Client) (string, any) { return "jsonResponseKey", "boards" },
	func(client api.Client) (string, any) { return "config", client.(*monday.Client).Config },
).SetName("GetGamesFromMonday")

GetGamesFromMonday is a api.Binding to retrieve multiple Game from the mapped board and group. Arguments provided to Execute:

• page (int): The page of results to retrieve. This means that GetGamesFromMonday can be passed to an api.Paginator.

• config (monday.Config): The monday.Config to use to find the monday.MappingConfig for the Game model.

• db (*gorm.DB): The gorm.DB instance to use to search for the Game's of the IDs found in the Monday items. This is only used in the Response method.

Execute returns a list of Game instances within their mapped board and group combination for the given page of results. It does this by retrieving the Game.ID from the appropriate column from each item and then searching the gorm.DB instance which is provided in the 3rd argument.

View Source
var GetSteamAppsFromMonday = api.NewBinding[monday.ItemResponse, []*SteamApp](
	func(b api.Binding[monday.ItemResponse, []*SteamApp], args ...any) api.Request {
		page := args[0].(int)
		mapping := args[1].(monday.Config).MondayMappingForModel(SteamApp{})
		boardIds := mapping.MappingBoardIDs()
		groupIds := mapping.MappingGroupIDs()
		return monday.GetItems.Request(page, boardIds, groupIds)
	},
	monday.ResponseWrapper[monday.ItemResponse, []*SteamApp],
	monday.ResponseUnwrapped[monday.ItemResponse, []*SteamApp],
	func(b api.Binding[monday.ItemResponse, []*SteamApp], response monday.ItemResponse, args ...any) []*SteamApp {
		items := monday.GetItems.Response(response)
		mapping := args[1].(monday.Config).MondayMappingForModel(SteamApp{})
		db := args[2].(*gorm.DB)
		apps := make([]*SteamApp, 0)
		for _, item := range items {
			columnMap := make(map[string]monday.ColumnValue)
			for _, column := range item.ColumnValues {
				columnMap[column.Id] = column
			}

			var (
				appID  uint64
				column monday.ColumnValue
				err    error
				ok     bool
			)
			if column, ok = columnMap[mapping.MappingModelInstanceIDColumnID()]; !ok {
				continue
			}

			app := SteamApp{}
			if appID, err = strconv.ParseUint(strings.Trim(column.Value, `"`), 10, 64); err != nil {
				continue
			}

			if err := db.Find(&app, "id = ?", appID).Error; errors.Is(err, gorm.ErrRecordNotFound) {
				continue
			}

			apps = append(apps, &app)

			var itemID int64
			if itemID, err = strconv.ParseInt(item.Id, 10, 64); err == nil {
				app.MondayItemID = int(itemID)
			}
			app.MondayBoardID = item.BoardId

			if column, ok = columnMap[mapping.MappingModelInstanceWatchedColumnID()]; ok {
				if column.Value != "null" {
					var watches monday.Votes
					if err = json.Unmarshal([]byte(column.Value), &watches); err == nil {
						if len(watches.VoterIds) > 0 {
							app.Watched = &item.Id
						}
					}
				} else {
					app.Watched = nil
				}
			}

			voteValues := []int{0, 0}
			for i, votesColumnID := range []string{
				mapping.MappingModelInstanceUpvotesColumnID(),
				mapping.MappingModelInstanceDownvotesColumnID(),
			} {
				var voteColumn monday.ColumnValue
				if voteColumn, ok = columnMap[votesColumnID]; !ok {
					continue
				}
				if voteColumn.Value == "null" {
					continue
				}
				var votes monday.Votes
				if err = json.Unmarshal([]byte(voteColumn.Value), &votes); err != nil {
					continue
				}
				voteValues[i] = len(votes.VoterIds)
			}
			app.Votes = int32(voteValues[0] - voteValues[1])
		}
		return apps
	}, func(binding api.Binding[monday.ItemResponse, []*SteamApp]) []api.BindingParam {
		return api.Params(
			"page", 0, true,
			"config", reflect.TypeOf((*monday.Config)(nil)), true,
			"db", &gorm.DB{}, true,
		)
	}, true,
	func(client api.Client) (string, any) { return "jsonResponseKey", "boards" },
	func(client api.Client) (string, any) { return "config", client.(*monday.Client).Config },
).SetName("GetSteamAppsFromMonday")

GetSteamAppsFromMonday is a api.Binding that retrieves all the SteamApp from the mapped board and group. Arguments provided to execute:

• page (int): The page of results to retrieve. This means that GetSteamAppsFromMonday can be passed to an api.Paginator.

• config (monday.Config): The monday.Config to use to find the monday.MappingConfig for the SteamApp model.

• db (*gorm.DB): The gorm.DB instance to use to search for the SteamApp's of the IDs found in the Monday items. This is only used in the Response method.

Execute returns a list of SteamApp instances within their mapped board and group combination for the given page of results. It does this by retrieving the SteamApp.ID from the appropriate column from each item and then searching the gorm.DB instance which is provided in the 3rd argument.

View Source
var UpdateGameInMonday = api.NewBinding[monday.ItemId, string](
	func(b api.Binding[monday.ItemId, string], args ...any) api.Request {
		game := args[0].(*Game)
		itemId := args[1].(int)
		boardId := args[2].(int)
		mapping := args[3].(monday.Config).MondayMappingForModel(Game{})
		columnValues, err := mapping.ColumnValues(game, mapping.MappingColumnsToUpdate()...)
		if err != nil {
			panic(err)
		}
		return monday.ChangeMultipleColumnValues.Request(
			itemId,
			boardId,
			columnValues,
		)
	},
	monday.ResponseWrapper[monday.ItemId, string],
	monday.ResponseUnwrapped[monday.ItemId, string],
	monday.ChangeMultipleColumnValues.GetResponseMethod(),
	func(binding api.Binding[monday.ItemId, string]) []api.BindingParam {
		return api.Params(
			"game", &Game{}, true,
			"itemId", 0, true,
			"boardId", 0, true,
			"config", reflect.TypeOf((*monday.Config)(nil)), true,
		)
	}, false,
	func(client api.Client) (string, any) { return "jsonResponseKey", "boards" },
	func(client api.Client) (string, any) { return "config", client.(*monday.Client).Config },
).SetName("UpdateGameInMonday")

UpdateGameInMonday is an api.Binding which updates the monday.Item of the given ID within the monday.Board of the given ID for Game using the monday.MappingConfig.ColumnValues method to generate values for all the monday.Column IDs provided by the monday.MappingConfig.MappingColumnsToUpdate method. Arguments provided to Execute:

• game (*Game): The Game to use as the basis for the new column values.

• itemId (int): The ID of the monday.Item that represents the given Game.

• boardId (int): The ID of the monday.Board within which the monday.Item resides.

• config (monday.Config): The monday.Config used to fetch the monday.MappingConfig for Game from. This monday.MappingConfig is then used to generate the column values that are posted to the Monday API to mutate the column values of the monday.Item of the given ID in the mapped monday.Board.

Execute returns the ID of the monday.Item that has been mutated.

View Source
var UpdateSteamAppInMonday = api.NewBinding[monday.ItemId, string](
	func(b api.Binding[monday.ItemId, string], args ...any) api.Request {
		app := args[0].(*SteamApp)
		itemId := args[1].(int)
		boardId := args[2].(int)
		mapping := args[3].(monday.Config).MondayMappingForModel(SteamApp{})
		columnValues, err := mapping.ColumnValues(app, mapping.MappingColumnsToUpdate()...)
		if err != nil {
			panic(err)
		}
		return monday.ChangeMultipleColumnValues.Request(
			itemId,
			boardId,
			columnValues,
		)
	},
	monday.ResponseWrapper[monday.ItemId, string],
	monday.ResponseUnwrapped[monday.ItemId, string],
	monday.ChangeMultipleColumnValues.GetResponseMethod(),
	func(binding api.Binding[monday.ItemId, string]) []api.BindingParam {
		return api.Params(
			"game", &SteamApp{}, true,
			"itemId", 0, true,
			"boardId", 0, true,
			"config", reflect.TypeOf((*monday.Config)(nil)), true,
		)
	}, false,
	func(client api.Client) (string, any) { return "jsonResponseKey", "change_multiple_column_values" },
	func(client api.Client) (string, any) { return "config", client.(*monday.Client).Config },
).SetName("UpdateSteamAppInMonday")

UpdateSteamAppInMonday is a api.Binding which updates the monday.Item of the given ID within the monday.Board of the given ID for SteamApp using the monday.MappingConfig.ColumnValues method to generate values for all the monday.Column IDs provided by the monday.MappingConfig.MappingColumnsToUpdate method. Arguments provided to Execute:

• game (*SteamApp): The SteamApp to use as the basis for the new column values.

• itemId (int): The ID of the monday.Item that represents the given SteamApp.

• boardId (int): The ID of the monday.Board within which the monday.Item resides.

• config (monday.Config): The monday.Config used to fetch the monday.MappingConfig for SteamApp from. This monday.MappingConfig is then used to generate the column values that are posted to the Monday API to mutate the column values of the monday.Item of the given ID in the mapped monday.Board.

Execute returns the ID of the monday.Item that has been mutated.

Functions

func CreateInitialSteamApps

func CreateInitialSteamApps(db *gorm.DB) (err error)

CreateInitialSteamApps will use the browser.SteamGetAppList to create initial SteamApp for each app on Steam if the number of SteamApp is 0. This should technically only ever run once on the creation of the SteamApp table.

func ScrapeStorefrontForGameModel

func ScrapeStorefrontForGameModel[ID comparable](id ID, gameModelScraper GameModelStorefrontScraper[ID], config ScrapeConfig)

ScrapeStorefrontForGameModel scrapes a single Storefront for a GameModel using their GameModelStorefrontScraper instance.

func SetScrapeConfig

func SetScrapeConfig(config ScrapeConfig)

SetScrapeConfig will set the package-wide ScrapeConfig variable for the models package.

Types

type ChartImage

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

func (*ChartImage) Base64

func (ci *ChartImage) Base64() string

func (*ChartImage) Bytes

func (ci *ChartImage) Bytes() []byte

func (*ChartImage) Img

func (ci *ChartImage) Img(alt string, style string) template.HTML

type CheckedWeightedModel

type CheckedWeightedModel interface {
	WeightedModel
	CheckCalculateWeightedScore() bool
}

type Developer

type Developer struct {
	// ID is the ID of the Twitter user that this developer corresponds to.
	ID string
	// Name is the name of the Twitter user that this developer corresponds to.
	Name string
	// Username is the username of the Twitter user that this developer corresponds to.
	Username string
	// Description is the bio of the Twitter user that this developer corresponds to.
	Description string
	// Type denotes whether we found this Developer on Twitter or Reddit.
	Type DeveloperType `gorm:"type:developer_type"`
	// ProfileCreated is when the Developer's Twitter profile was created. Note how we avoid using CreatedAt.
	ProfileCreated time.Time
	// PublicMetrics is the most up-to-date public metrics for this Developer's Twitter profile.
	PublicMetrics *twitter.UserMetricsObj `gorm:"embedded;embeddedPrefix:current_"`
	// RedditPublicMetrics contains the user's karma if they are a RedditDeveloperType Developer.
	RedditPublicMetrics *RedditUserMetrics `gorm:"embedded;embeddedPrefix:current_reddit_"`
	// UpdatedAt is when this developer was updated. So we know up until when the PublicMetrics are fresh to.
	UpdatedAt time.Time
	// TimesHighlighted is the number of times this Developer has been highlighted by the Measure phase.
	TimesHighlighted int32
	// Disabled represents whether this Developer should be included in the Update phase of the Scout procedure.
	Disabled bool
}

Developer represents a potential indie developer's Twitter account. This also contains some current metrics for their profile.

func (*Developer) DeveloperSnapshots

func (d *Developer) DeveloperSnapshots(db *gorm.DB) (developerSnapshots []*DeveloperSnapshot, err error)

DeveloperSnapshots will find all the DeveloperSnapshot for this Developer. The resulting array is ordered by the DeveloperSnapshot.Version ascending.

func (*Developer) Games

func (d *Developer) Games(db *gorm.DB) (games []*Game, err error)

Games returns the games for this developer ordered by weighted score descending.

func (*Developer) GetObservedName

func (d *Developer) GetObservedName() string

func (*Developer) GetVariableNames

func (d *Developer) GetVariableNames() []string

func (*Developer) LatestDeveloperSnapshot

func (d *Developer) LatestDeveloperSnapshot(db *gorm.DB) (developerSnap *DeveloperSnapshot, err error)

LatestDeveloperSnapshot will get the latest DeveloperSnapshot for this Developer.

func (d *Developer) Link() string

Link returns the URL link to the Developer's Twitter page.

func (*Developer) OnConflict

func (d *Developer) OnConflict() clause.OnConflict

OnConflict returns the clause.OnConflict that should be checked in an upsert clause.

func (*Developer) OnCreateOmit

func (d *Developer) OnCreateOmit() []string

OnCreateOmit returns the fields that should be omitted when creating a Developer.

func (*Developer) String

func (d *Developer) String() string

String returns a string representation of the Developer's Username, ID, and Disabled fields.

func (*Developer) Train

func (d *Developer) Train(trend *Trend) (err error)

func (*Developer) Trend

func (d *Developer) Trend(db *gorm.DB) (trend *Trend, err error)

Trend returns the Trend that has been trained on the DeveloperSnapshot for the referred to Developer.

func (*Developer) TrendingDev

func (d *Developer) TrendingDev(db *gorm.DB) (trendingDev *TrendingDev, err error)

TrendingDev returns the TrendingDev that is comprised from the Developer's Games, DeveloperSnapshots, and Trend.

func (*Developer) TypedUsername

func (d *Developer) TypedUsername() string

TypedUsername returns the Developer's Username prefixed with the string representation of Type.

type DeveloperMinimal

type DeveloperMinimal struct {
	// ID is the ID of the Twitter user that this developer corresponds to.
	ID string
	// Type denotes whether we found this Developer on Twitter or Reddit.
	Type DeveloperType
}

DeveloperMinimal is used within the state cache to save details for a developer that has been updated, disabled, or enabled.

type DeveloperSnapshot

type DeveloperSnapshot struct {
	// ID of the snapshot that is automatically generated by postgres.
	ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
	// CreatedAt is automatically set to the time that this record is created.
	CreatedAt time.Time
	// Version is the number of this snapshot out of the set of all DeveloperSnapshot for this Developer.
	Version int32
	// TimesHighlighted is the number of times the Developer for this DeveloperSnapshot has been highlighted by the
	// Measure phase.
	TimesHighlighted int32
	// DeveloperID is the foreign key to the Developer this snapshot is for.
	DeveloperID string
	Developer   *Developer `gorm:"constraint:OnDelete:CASCADE;"`
	// Tweets is the number of tweets/Reddit posts that could be scraped for this Developer for this DeveloperSnapshot.
	Tweets int32
	// TweetIDs is the IDs of the Tweets captured by this DeveloperSnapshot. Using these, we can generate links for each
	// Tweet.
	TweetIDs pq.StringArray `gorm:"type:varchar(64)[];default:'{}'"`
	// RedditPostIDs are subreddit name, post ID pairs captured by this DeveloperSnapshot. I.e. a subreddit name
	// followed by a Reddit post ID. Using these, we can generate links for each post.
	RedditPostIDs pq.StringArray `gorm:"type:varchar(32)[];default:'{}'"`
	// TweetTimeRange is the time.Duration between the Developer's earliest tweet/post (that was scraped) and their
	// latest tweet/post (that was scraped). This is set to nil when only one tweet/post was scraped.
	TweetTimeRange NullDuration
	// LastTweetTime is the time of the last tweet/post that was scraped.
	LastTweetTime time.Time
	// TweetTimeRange is the average time.Duration between all the Developer's tweets/posts that were scraped for this
	// DeveloperSnapshot. This is set to nil when only one tweet/post was scraped.
	AverageDurationBetweenTweets NullDuration
	// TweetsPublicMetrics is the sum of all public metrics for the tweets that were scraped for this Developer for this
	// DeveloperSnapshot.
	TweetsPublicMetrics *twitter.TweetMetricsObj `gorm:"embedded;embeddedPrefix:total_tweet_"`
	// PostPublicMetrics is the sum of all public metrics for the Reddit posts that were scraped for this Developer for
	// this DeveloperSnapshot. This only applies if the Developer is of the RedditDeveloperType.
	PostPublicMetrics *RedditPostMetrics `gorm:"embedded;embeddedPrefix:total_post_"`
	// UserPublicMetrics is the public metrics for the Developer at the time this DeveloperSnapshot was created.
	UserPublicMetrics *twitter.UserMetricsObj `gorm:"embedded;embeddedPrefix:user_"`
	// RedditPublicMetrics contains the Developer's karma at the time this DeveloperSnapshot was created (if they are a
	// RedditDeveloperType Developer).
	RedditPublicMetrics *RedditUserMetrics `gorm:"embedded;embeddedPrefix:reddit_user_"`
	// ContextAnnotationSet is the set of all ContextAnnotations for all tweets that were scraped for this Developer for
	// this DeveloperSnapshot.
	ContextAnnotationSet *myTwitter.ContextAnnotationSet
	// Games is the number of Game that are currently found for this Developer at the time this DeveloperSnapshot was
	// created. This is a computed field, no need to set it before saving.
	Games int32
	// GameWeightedScoresSum is the sum of all Game.WeightedScore values for all the Game that are currently found for
	// this Developer at the time this DeveloperSnapshot was created. This is a computed field, no need to set it before
	// saving.
	GameWeightedScoresSum float64
	// WeightedScore is a weighted average comprised of the values taken from Tweets, TweetTimeRange,
	// AverageDurationBetweenTweets, TweetsPublicMetrics, UserPublicMetrics, ContextAnnotationSet, Games, and
	// GameWeightedScoresSum. This is a computed field, no need to set it before saving.
	WeightedScore float64
}

DeveloperSnapshot is snapshot of a potential developer's public user metrics, aggregations of tweet metrics

func (*DeveloperSnapshot) BeforeCreate

func (ds *DeveloperSnapshot) BeforeCreate(tx *gorm.DB) (err error)

func (*DeveloperSnapshot) BeforeUpdate

func (ds *DeveloperSnapshot) BeforeUpdate(tx *gorm.DB) (err error)

func (*DeveloperSnapshot) Empty

func (ds *DeveloperSnapshot) Empty() any

func (*DeveloperSnapshot) OnConflict

func (ds *DeveloperSnapshot) OnConflict() clause.OnConflict

OnConflict returns the clause.OnConflict that should be checked in an upsert clause.

func (*DeveloperSnapshot) OnCreateOmit

func (ds *DeveloperSnapshot) OnCreateOmit() []string

OnCreateOmit returns the fields that should be omitted when creating a DeveloperSnapshot.

func (*DeveloperSnapshot) Order

func (ds *DeveloperSnapshot) Order() string

func (*DeveloperSnapshot) UpdateComputedFields

func (ds *DeveloperSnapshot) UpdateComputedFields(tx *gorm.DB) (err error)

type DeveloperType

type DeveloperType string
const (
	UnknownDeveloperType DeveloperType = "U"
	TwitterDeveloperType DeveloperType = "T"
	RedditDeveloperType  DeveloperType = "R"
)

func DevTypeFromUsername

func DevTypeFromUsername(username string) (DeveloperType, string)

DevTypeFromUsername returns the DeveloperType of the given Developer.Username, as well as the given Developer.Username without the prefixed DeveloperType character.

func (DeveloperType) EnumValue

func (dt DeveloperType) EnumValue() string

func (DeveloperType) Index

func (dt DeveloperType) Index() int

Index returns the index of the DeveloperType.

func (*DeveloperType) Scan

func (dt *DeveloperType) Scan(value interface{}) error

func (DeveloperType) String

func (dt DeveloperType) String() string

String returns the formal name of the DeveloperType.

func (DeveloperType) Type

func (dt DeveloperType) Type() string

func (DeveloperType) Types

func (dt DeveloperType) Types() []DeveloperType

func (DeveloperType) Value

func (dt DeveloperType) Value() (driver.Value, error)

func (DeveloperType) Values

func (dt DeveloperType) Values() []string

type DisableEnableDeleteStats

type DisableEnableDeleteStats struct {
	// EnabledDevelopersBefore is the number of enabled developers before the phase.
	EnabledDevelopersBefore int64
	// DisabledDevelopersBefore is the number of disabled developers before the phase.
	DisabledDevelopersBefore int64
	// EnabledDevelopersAfter is the number of enabled developers after the phase.
	EnabledDevelopersAfter int64
	// DisabledDevelopersAfter is the number of enabled developers after the phase.
	DisabledDevelopersAfter int64
	// DeletedDevelopers is the number of developers that were deleted in the phase. Only applies for
	// ScoutResult.DeleteStats.
	DeletedDevelopers int64
	// TotalSampledDevelopers is the total number of developers that were sampled in the phase. Only applies for
	// ScoutResult.EnableStats and ScoutResult.DeleteStats.
	TotalSampledDevelopers int64
	// TotalFinishedSamples the number of Developer samples successfully processed in the phase. Only applies for
	// ScoutResult.EnableStats and ScoutResult.DeleteStats.
	TotalFinishedSamples int64
}

func (*DisableEnableDeleteStats) After

func (d *DisableEnableDeleteStats) After(db *gorm.DB) error

After sets the EnabledDevelopersAfter and DisabledDevelopersAfter fields using SQL queries. Call this after the Disable, Enable, and Delete Phase logic.

func (*DisableEnableDeleteStats) Before

func (d *DisableEnableDeleteStats) Before(db *gorm.DB) error

Before sets the EnabledDevelopersBefore and DisabledDevelopersBefore fields using SQL queries. Call this before the Disable, Enable, and Delete Phase logic.

type DiscoveryUpdateSnapshotStats

type DiscoveryUpdateSnapshotStats struct {
	// Developers is the number of Developers created when in the DiscoveryPhase, the number of Developers updated when
	// in the UpdatePhase, and the number of Developers that have partial snapshots in the SnapshotPhase.
	Developers int64
	// Games is the number of Game created when in the DiscoveryPhase, the number of Games updated when in the
	// UpdatePhase, and the number of Games within the GameIDs cached field in the SnapshotPhase.
	Games int64
	// TweetsConsumed is the number of Tweets successfully consumed from the Twitter API.
	TweetsConsumed int64
	// PostsConsumed is the number of Reddit posts successfully consumed from the Reddit API.
	PostsConsumed int64
	// TotalSnapshots is the total number of partial DeveloperSnapshot before merging for each developer.
	TotalSnapshots int64
	// SnapshotsCreated is the total number of DeveloperSnapshot created. Only applies for ScoutResult.SnapshotStats.
	SnapshotsCreated int64
}

type EvaluatorConfig

type EvaluatorConfig interface {
	EvaluatorModelName() string
	EvaluatorField() string
	EvaluatorExpression() string
	EvaluatorCompiledExpression() *vm.Program
	WeightAndInverse() (float64, bool)
	ModelType() reflect.Type
	ModelField(modelInstance any) any
	Env(modelInstance any) map[string]any
	Eval(modelInstance any) ([]float64, error)
}

type Game

type Game struct {
	// ID is an uuid.UUID that is automatically generated by postgres.
	ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
	// MondayItemID is a non-database field that is filled when fetching the Game from Monday using the GetGamesFromMonday
	// monday.Binding.
	MondayItemID int `gorm:"-:all"`
	// MondayBoardID is a non-database field that is filled when fetching the Game from Monday using the
	// GetGamesFromMonday monday.Binding.
	MondayBoardID string `gorm:"-:all"`
	// IsGame is only used for our benefit to check if the software that is being sold on the storepage pointed to by
	// Website is a Game. We only want to store Games within the DB and not DLC, Assets, Software, etc.
	IsGame bool `gorm:"-:all"`
	// Name is the name of the game. If this cannot be found it is set to nil.
	Name null.String
	// Storefront is where this game is sold. If this cannot be found it is set to nil.
	Storefront Storefront `gorm:"type:storefront_type"`
	// Website is the URL for this game's website. Usually a Steam store page.
	Website null.String `gorm:"index:idx_website,where:website is not null,unique"`
	// ImageURL is the URL for this game's cover/header image. For Game's whose Storefront is SteamStorefront this is
	// set to: "https://cdn.cloudflare.steamstatic.com/steam/apps/{{ $appid }}/header.jpg", and for ItchIOStorefront
	// Games this is set to the URL of the header image on the Game's Itch.IO page.
	ImageURL null.String `gorm:"default:null"`
	// Developers are the Twitter/Reddit usernames that could be developers for this Game. Each username is prefixed with
	// their Developer.Type's character (i.e. "T" or "R").
	Developers pq.StringArray `gorm:"type:varchar(21)[];default:'{}'"`
	// VerifiedDeveloperUsernames are the Twitter/Reddit usernames that can be found somewhere on the Game's Website.
	// Each username is prefixed with their Developer.Type's character (i.e. "T" or "R").
	VerifiedDeveloperUsernames pq.StringArray `gorm:"type:varchar(21)[];default:'{}'"`
	// Updates is the number of times this app has been updated. 0 means that the Game has just been created. Please
	// don't set this yourself when creating a Game.
	Updates uint64
	// ReleaseDate is when the game was/is going to be released. If this cannot be found, then it is set to nil.
	ReleaseDate null.Time
	// Publisher is the publisher for this game. Usually found via the Steam store API. If this cannot be found it is
	// set to nil. If this is set then it negatively contributes to the Game's WeightedScore.
	Publisher null.String
	// TotalReviews for this game. Only set when Storefront is SteamStorefront. If this cannot be found it is set to
	// nil.
	TotalReviews null.Int32
	// PositiveReviews for this game. Only set when Storefront is SteamStorefront. If this cannot be found it is set to
	// nil.
	PositiveReviews null.Int32
	// NegativeReviews for this game. Only set when Storefront is SteamStorefront. If this cannot be found it is set to
	// nil.
	NegativeReviews null.Int32
	// ReviewScore for this game (PositiveReviews / TotalReviews). Only set when Storefront is SteamStorefront,
	// PositiveReviews is set, and NegativeReviews is set. This is a computed field, no need to set it before saving.
	ReviewScore null.Float64
	// TotalUpvotes for this game. Only set when Storefront is SteamStorefront.
	TotalUpvotes null.Int32
	// TotalDownvotes for this game. Only set when Storefront is SteamStorefront.
	TotalDownvotes null.Int32
	// TotalComments for this game. Only set when Storefront is SteamStorefront or ItchIOStorefront.
	TotalComments null.Int32
	// TagScore is average value of each tag. Each tag's value is calculated by multiplying the number of upvotes for
	// that tag by the default value/override value for that tag. See TagConfig for more info.
	TagScore null.Float64
	// Watched indicates the Monday.com item Id, from the connected Monday board, for the Game. If this is nil, then it
	// can be assumed that this Game is not being watched.
	Watched *string `gorm:"default:null;type:varchar(32)"`
	// Votes is the number of upvotes for the Game on the linked Monday board/group minus the number of downvotes.
	Votes int32 `gorm:"default:0"`
	// WeightedScore is a weighted average comprised of the values taken from Publisher, TotalReviews, ReviewScore,
	// TotalUpvotes, TotalDownvotes, TotalComments, TagScore, and Updates for this game. If
	// Game.CheckCalculateWeightedScore is false then this will be nil. This is a computed field, no need to set it
	// before saving.
	WeightedScore null.Float64
}

Game represents a game (supposedly) being developed by a Developer. The Website of which can be on one of many Storefront.

func (*Game) Advocates

func (g *Game) Advocates(db *gorm.DB) []*Developer

func (*Game) BeforeCreate

func (g *Game) BeforeCreate(tx *gorm.DB) (err error)

func (*Game) BeforeUpdate

func (g *Game) BeforeUpdate(tx *gorm.DB) (err error)

func (*Game) CheckCalculateWeightedScore

func (g *Game) CheckCalculateWeightedScore() bool

CheckCalculateWeightedScore checks if we can calculate the WeightedScore for this Game. This is dependent on the Website field being set and the Storefront not being UnknownStorefront.

func (*Game) Empty

func (g *Game) Empty() any

func (*Game) GetID

func (g *Game) GetID() string

func (*Game) GetVerifiedDeveloperUsernames

func (g *Game) GetVerifiedDeveloperUsernames() []string

func (*Game) OnConflict

func (g *Game) OnConflict() clause.OnConflict

OnConflict returns the clause.OnConflict that should be checked in an upsert clause.

func (*Game) OnCreateOmit

func (g *Game) OnCreateOmit() []string

OnCreateOmit returns the fields that should be omitted when creating a Game.

func (*Game) Order

func (g *Game) Order() string

func (*Game) String

func (g *Game) String() string

func (*Game) Update

func (g *Game) Update(db *gorm.DB, config ScrapeConfig) error

Update will update the Game. It does this by calling the Storefront.ScrapeGame method on the referred to Game.

func (*Game) UpdateComputedFields

func (g *Game) UpdateComputedFields(tx *gorm.DB) (err error)

UpdateComputedFields will update the fields in Game that are computed.

func (*Game) VerifiedDeveloper

func (g *Game) VerifiedDeveloper(db *gorm.DB) *Developer

VerifiedDeveloper returns the first verified Developer for this Game. If there are none, we will return a nil pointer.

func (*Game) Wrapper

func (g *Game) Wrapper() GameModelWrapper[string, GameModel[string]]

type GameItchIOStorefront

type GameItchIOStorefront GameWrapper

func (*GameItchIOStorefront) AfterScrape

func (g *GameItchIOStorefront) AfterScrape(args ...any)

func (*GameItchIOStorefront) Args

func (g *GameItchIOStorefront) Args(url string) []any

func (*GameItchIOStorefront) GetStorefront

func (g *GameItchIOStorefront) GetStorefront() Storefront

func (*GameItchIOStorefront) ScrapeCommunity

func (g *GameItchIOStorefront) ScrapeCommunity(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*GameItchIOStorefront) ScrapeExtra

func (g *GameItchIOStorefront) ScrapeExtra(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*GameItchIOStorefront) ScrapeInfo

func (g *GameItchIOStorefront) ScrapeInfo(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*GameItchIOStorefront) ScrapeReviews

func (g *GameItchIOStorefront) ScrapeReviews(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*GameItchIOStorefront) ScrapeTags

func (g *GameItchIOStorefront) ScrapeTags(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

type GameModel

type GameModel[ID comparable] interface {
	// Update will update a game model instance by running the scrape procedures for its GameModelWrapper.
	Update(db *gorm.DB, config ScrapeConfig) error
	// Wrapper returns the GameModelWrapper for this GameModel.
	Wrapper() GameModelWrapper[ID, GameModel[ID]]
	// GetID returns the unique ID value of the GameModel.
	GetID() ID
}

GameModel should be implemented by the gorm.Model that represents a table of games.

func ScrapeStorefrontsForGameModel

func ScrapeStorefrontsForGameModel[ID comparable](gameModel GameModel[ID], storefrontIDs map[Storefront]mapset.Set[ID], config ScrapeConfig) GameModel[ID]

ScrapeStorefrontsForGameModel will run the scrape procedure for all the Storefront that exist in the given map. Each Storefront can have a set of IDs/URLs. However, only one of these will ever be scraped and this is chosen at random.

type GameModelStorefrontScraper

type GameModelStorefrontScraper[ID comparable] interface {
	// Args returns the arguments to be used for the scrape functions for the ScrapableGameSource(s) as well as the
	// arguments for AfterScrape.
	Args(id ID) []any
	// GetStorefront returns the Storefront that this scraper scrapes.
	GetStorefront() Storefront
	ScrapeInfo(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error
	ScrapeReviews(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error
	ScrapeTags(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error
	ScrapeCommunity(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error
	ScrapeExtra(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error
	// AfterScrape is called after all the above methods have completed for a Storefront. It is usually used to set the
	// Storefront and Website of the internal game instance using the given args extracted from Args.
	AfterScrape(args ...any)
}

GameModelStorefrontScraper should implement the scrape procedures for the appropriate ScrapableGameSource(s) for a single Storefront. Each function should modify a reference to an internal game model. Therefore, the structure of the implementor should resemble (and in most cases match) the structure of the implementor of the GameModelWrapper for the same game model.

func UnscrapableError

func UnscrapableError[ID comparable](storefront Storefront) GameModelStorefrontScraper[ID]

UnscrapableError returns the UnscrapableStorefront error as a GameModelStorefrontScraper to be used as a default case in the implemented GameModelWrapper.StorefrontWrapper methods in case a Storefront cannot be scraped.

type GameModelWrapper

type GameModelWrapper[ID comparable, GMT GameModel[ID]] interface {
	// Default will set the fields of the internal game model instance to their default values as well as create the
	// internal game model instance if it is nil.
	Default()
	// Nil will set the internal game model instance to nil.
	Nil()
	// Get will return the GameModel instance that is referenced internally.
	Get() GMT
	// StorefrontScraper will return the GameModelStorefrontScraper for the given Storefront. This will allow you to
	// scrape the information for the internal game model instance off of this Storefront.
	StorefrontScraper(storefront Storefront) GameModelStorefrontScraper[ID]
}

GameModelWrapper should be implemented by a struct containing a reference to a GameModel. With this interface you can access the GameModelStorefrontScraper(s) for each Storefront as well as setting the Default state of the GameModel instance, nilling out the GameModel instance, or retrieving the GameModel instance. ID is the type of the ID that is used to represent a unique GameModel instance. For example, Game.Website is the unique ID used for Game, so the implemented type will use string in its type parameters.

type GameSteamStorefront

type GameSteamStorefront GameWrapper

func (*GameSteamStorefront) AfterScrape

func (g *GameSteamStorefront) AfterScrape(args ...any)

func (*GameSteamStorefront) Args

func (g *GameSteamStorefront) Args(url string) []any

func (*GameSteamStorefront) GetStorefront

func (g *GameSteamStorefront) GetStorefront() Storefront

func (*GameSteamStorefront) ScrapeCommunity

func (g *GameSteamStorefront) ScrapeCommunity(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) (err error)

func (*GameSteamStorefront) ScrapeExtra

func (g *GameSteamStorefront) ScrapeExtra(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) (err error)

func (*GameSteamStorefront) ScrapeInfo

func (g *GameSteamStorefront) ScrapeInfo(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*GameSteamStorefront) ScrapeReviews

func (g *GameSteamStorefront) ScrapeReviews(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*GameSteamStorefront) ScrapeTags

func (g *GameSteamStorefront) ScrapeTags(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) (err error)

type GameWrapper

type GameWrapper struct{ Game *Game }

func (*GameWrapper) Default

func (g *GameWrapper) Default()

func (*GameWrapper) Get

func (g *GameWrapper) Get() GameModel[string]

func (*GameWrapper) Nil

func (g *GameWrapper) Nil()

func (*GameWrapper) StorefrontScraper

func (g *GameWrapper) StorefrontScraper(storefront Storefront) GameModelStorefrontScraper[string]

type MeasureStats

type MeasureStats struct {
	// SampledTrendingDevelopers is the number of Developer sampled to create the email.Measure email.
	SampledTrendingDevelopers int64
	// EmailSendTimeTaken is the time it took to send the email.Measure email.
	EmailSendTimeTaken time.Duration
	// EmailSize is the generated email.Measure email's size in bytes.
	EmailSize int64
}

type NullDuration

type NullDuration struct {
	sql.NullInt64
}

NullDuration represents a nullable version of time.Duration.

func NullDurationFrom

func NullDurationFrom(duration time.Duration) NullDuration

NullDurationFrom constructs a new NullDuration from the given time.Duration. This will never be null.

func NullDurationFromPtr

func NullDurationFromPtr(duration *time.Duration) NullDuration

NullDurationFromPtr constructs a new NullDuration from the given pointer to a time.Duration.

func (*NullDuration) FromDurationPtr

func (nd *NullDuration) FromDurationPtr(duration *time.Duration)

FromDurationPtr sets the referred to NullDuration to the given time.Duration, setting NullDuration.NullInt64.Valid appropriately.

func (*NullDuration) IsValid

func (nd *NullDuration) IsValid() bool

func (*NullDuration) Ptr

func (nd *NullDuration) Ptr() *time.Duration

type RedditPostMetrics

type RedditPostMetrics struct {
	Ups                  int     `json:"ups"`
	Downs                int     `json:"downs"`
	Score                int     `json:"score"`
	UpvoteRatio          float32 `json:"upvote_ratio"`
	NumberOfComments     int     `json:"num_comments"`
	SubredditSubscribers int     `json:"subreddit_subscribers"`
}

type RedditUserMetrics

type RedditUserMetrics struct {
	PostKarma    int
	CommentKarma int
}

type ScoutResult

type ScoutResult struct {
	// ID of this ScoutResult that is automatically generated by postgres.
	ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
	// CreatedAt is when this ScoutResult was created.
	CreatedAt time.Time
	// Started is when the Scout procedure was started.
	Started time.Time
	// DiscoveryStats is the stats for the DiscoveryPhase.
	DiscoveryStats *DiscoveryUpdateSnapshotStats `gorm:"embedded;embeddedPrefix:discovery_"`
	// UpdateStats is the stats for the UpdatePhase.
	UpdateStats *DiscoveryUpdateSnapshotStats `gorm:"embedded;embeddedPrefix:update_"`
	// SnapshotStats is the stats for the SnapshotPhase.
	SnapshotStats *DiscoveryUpdateSnapshotStats `gorm:"embedded;embeddedPrefix:snapshot_"`
	// DisableStats is the stats for the DisablePhase.
	DisableStats *DisableEnableDeleteStats `gorm:"embedded;embeddedPrefix:disable_"`
	// EnableStats is the stats for the EnablePhase.
	EnableStats *DisableEnableDeleteStats `gorm:"embedded;embeddedPrefix:enable_"`
	// DeleteStats is the stats for the DeletePhase.
	DeleteStats *DisableEnableDeleteStats `gorm:"embedded;embeddedPrefix:delete_"`
	// MeasureStats is the stats for the MeasurePhase.
	MeasureStats *MeasureStats `gorm:"embedded;embeddedPrefix:measure_"`
}

type ScrapableGameSource

type ScrapableGameSource int

ScrapableGameSource represents a source that can be scraped for a GameModelStorefrontScraper for a Storefront.

const (
	// Info fetches general information on the game. Such as the game's name and publisher.
	Info ScrapableGameSource = iota
	// Reviews fetches information surrounding reviews for the game. Such as total positive and negative reviews.
	Reviews
	// Community fetches information surrounding the community for a game. Such as community post upvotes and downvotes.
	Community
	// Tags fetches information surrounding the game's tags or genre.
	Tags
	// Extra is any extra information that needs to be fetched that is not in one of the above
	Extra
)

func (ScrapableGameSource) Sources

func (sgs ScrapableGameSource) Sources() []ScrapableGameSource

Sources returns all the possible ScrapableGameSource.

func (ScrapableGameSource) String

func (sgs ScrapableGameSource) String() string

String returns the name of the ScrapableGameSource.

type ScrapeConfig

type ScrapeConfig interface {
	ScrapeDebug() bool
	ScrapeStorefronts() []StorefrontConfig
	ScrapeGetStorefront(storefront Storefront) StorefrontConfig
	ScrapeConstants() ScrapeConstants
	ScrapeFieldEvaluatorForWeightedModelField(model any, field string) (evaluator EvaluatorConfig, err error)
	ScrapeEvalForWeightedModelField(modelInstance any, field string) (values []float64, err error)
	ScrapeWeightedModelCalc(modelInstance any) (weightedAverage float64, err error)
}

ScrapeConfig contains the configuration for the scrape.

type ScrapeConstants

type ScrapeConstants interface {
	DefaultMaxTries() int
	DefaultMinDelay() time.Duration
}

type SetOrAddFunc

type SetOrAddFunc int

SetOrAddFunc represents a function that can be supplied to the ScoutState State cached field's SetOrAdd method for the Result field. For instance, to increment the DiscoveryStats.Developers field:

state.GetCachedField(StateType).SetOrAdd("Result", "DiscoveryStats", "Developers", SetOrAddInc.Func())
const (
	// SetOrAddInc returns a function to increment the field's value by 1.
	SetOrAddInc SetOrAddFunc = iota
	// SetOrAddAdd returns a function to add the field's value to the value specified as the first argument to
	// SetOrAddFunc.Func.
	SetOrAddAdd
)

func (SetOrAddFunc) Func

func (sora SetOrAddFunc) Func(args ...any) any

Func returns the function that should be used as the last argument to SetOrAdd. The provided arguments are used within the definitions for the functions for various reasons.

type SteamApp

type SteamApp struct {
	// ID directly matches a Steam app's appID.
	ID uint64
	// Name is the current name of the SteamApp.
	Name string
	// MondayItemID is a non-database field that is filled when fetching the SteamApp from Monday using the
	// GetSteamAppsFromMonday monday.Binding.
	MondayItemID int `gorm:"-:all"`
	// MondayBoardID is a non-database field that is filled when fetching the SteamApp from Monday using the
	// GetSteamAppsFromMonday monday.Binding.
	MondayBoardID string `gorm:"-:all"`
	// Type is only used for our benefit to check what type of software this SteamApp is. We do not save it to the
	// database at all, because we only save SteamApp's that are games.
	Type string `gorm:"-:all"`
	// CreatedAt is when this SteamApp was first created. SteamApp's that were CreatedAt closer to the current update
	// time are desired more than SteamApp's that were CreatedAt further in the past.
	CreatedAt time.Time `gorm:"<-:create"`
	// Bare is set when the SteamApp is bare version of the app fetched from SteamGetAppList. When set, the SteamApp
	// will only have ID and Name set to not be default.
	Bare bool
	// Updates is the number of times this SteamApp has been updated in the DB. This is not the number of changelist
	// that have occurred for this title.
	Updates uint64
	// DeveloperID is the foreign key to a Developer on Twitter. This can be nil/null, in case there is no socials
	// linked on the SteamApp's store page.
	DeveloperID *string
	Developer   *Developer `gorm:"constraint:OnDelete:SET NULL;"`
	// ReleaseDate is when this game was/is going to be released on SteamStorefront.
	ReleaseDate time.Time
	// OwnersOnly indicates whether the games details can only be seen by owners of the game. If this is true then it
	// negatively impacts this SteamApp's WeightedScore.
	OwnersOnly bool
	// HasStorepage indicates whether this game has a storepage on Steam. If this is false then it negatively impacts
	// this SteamApp's WeightedScore.
	HasStorepage bool
	// Publisher is the publisher for this game. If this cannot be found it is set to nil. If this is set then it
	// negatively contributes to the SteamApp's WeightedScore.
	Publisher null.String
	// TotalReviews for this SteamApp.
	TotalReviews int32
	// PositiveReviews for this SteamApp.
	PositiveReviews int32
	// NegativeReviews for this SteamApp.
	NegativeReviews int32
	// ReviewScore for this game (PositiveReviews / TotalReviews). This is a computed field, no need to set it before
	// saving.
	ReviewScore float64
	// TotalUpvotes for this SteamApp on news posts from the developer on Steam community.
	TotalUpvotes int32
	// TotalDownvotes for this SteamApp on news posts from the developer on Steam community.
	TotalDownvotes int32
	// TotalComments for this SteamApp on news posts from the developer on Steam community.
	TotalComments int32
	// TagScore is average value of each community voted tag for the SteamApp. Each tag's value is calculated by
	// multiplying the number of upvotes for that tag by the default value/override value for that tag. See TagConfig for
	// more info.
	TagScore float64
	// AssetModifiedTime is the last time an asset was modified on this SteamApp's store page.
	AssetModifiedTime time.Time
	// LastChangelistID is the ID of the last changelist that was read for this title.
	LastChangelistID uint64
	// TimesHighlighted is the number of times this SteamApp has been highlighted by the measure command.
	TimesHighlighted int32
	// Watched indicates the Monday.com item Id, from the connected Monday board, for the SteamApp. If this is nil, then
	// it can be assumed that this SteamApp is not being watched.
	Watched *string `gorm:"default:null;type:varchar(32)"`
	// Votes is the number of upvotes for the SteamApp on the linked Monday board/group minus the number of downvotes.
	Votes int32 `gorm:"default:0"`
	// WeightedScore is a weighted average comprised of the values taken from CreatedAt, Updates, ReleaseDate,
	// Publisher, TotalReviews, ReviewScore, TotalUpvotes, TotalDownvotes, TotalComments, TagScore, AssetModifiedTime,
	// and TimesHighlighted for the SteamApp. If SteamApp.CheckCalculateWeightedScore is false then this will be nil.
	// This is a computed field, no need to set it before saving.
	WeightedScore null.Float64
}

SteamApp represents an app on Steam that has been consumed from the ScoutWebPipes co-process. It can be created/updated in the websocket client that reads info pushed from the ScoutWebPipes process.

func (*SteamApp) BeforeCreate

func (app *SteamApp) BeforeCreate(tx *gorm.DB) (err error)

func (*SteamApp) BeforeUpdate

func (app *SteamApp) BeforeUpdate(tx *gorm.DB) (err error)

func (*SteamApp) CheckCalculateWeightedScore

func (app *SteamApp) CheckCalculateWeightedScore() bool

CheckCalculateWeightedScore will only return true when the SteamApp is not Bare, has no Publisher, and has a name. This is to stop the calculation of weights for games that have not yet been scraped/aren't fully initialised by Steam's backend. CreateInitialSteamApps will create SteamApp(s) with their Bare field set.

func (*SteamApp) Empty

func (app *SteamApp) Empty() any

func (*SteamApp) GetID

func (app *SteamApp) GetID() uint64

func (*SteamApp) OnConflict

func (app *SteamApp) OnConflict() clause.OnConflict

OnConflict returns the clause.OnConflict that should be checked in an upsert clause. For SteamApp the clause.OnConflict.DoUpdates field will be set as the Updates will need to be incremented and the CreatedAt field excluded from being updated.

func (*SteamApp) OnCreateOmit

func (app *SteamApp) OnCreateOmit() []string

OnCreateOmit returns the fields that should be omitted when creating a SteamApp.

func (*SteamApp) Order

func (app *SteamApp) Order() string

func (*SteamApp) String

func (app *SteamApp) String() string

func (*SteamApp) Update

func (app *SteamApp) Update(db *gorm.DB, config ScrapeConfig) error

func (*SteamApp) UpdateComputedFields

func (app *SteamApp) UpdateComputedFields(tx *gorm.DB) (err error)

UpdateComputedFields will update the fields in a SteamApp that are computed.

func (*SteamApp) VerifiedDeveloper

func (app *SteamApp) VerifiedDeveloper(db *gorm.DB) *Developer

VerifiedDeveloper returns the first verified Developer for this SteamApp. If there is not one, we will return a nil pointer.

func (*SteamApp) Website

func (app *SteamApp) Website() string

func (*SteamApp) Wrapper

func (app *SteamApp) Wrapper() GameModelWrapper[uint64, GameModel[uint64]]

type SteamAppSteamStorefront

type SteamAppSteamStorefront SteamAppWrapper

func (*SteamAppSteamStorefront) AfterScrape

func (s *SteamAppSteamStorefront) AfterScrape(args ...any)

func (*SteamAppSteamStorefront) Args

func (s *SteamAppSteamStorefront) Args(id uint64) []any

func (*SteamAppSteamStorefront) GetStorefront

func (s *SteamAppSteamStorefront) GetStorefront() Storefront

func (*SteamAppSteamStorefront) ScrapeCommunity

func (s *SteamAppSteamStorefront) ScrapeCommunity(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) (err error)

func (*SteamAppSteamStorefront) ScrapeExtra

func (s *SteamAppSteamStorefront) ScrapeExtra(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) (err error)

func (*SteamAppSteamStorefront) ScrapeInfo

func (s *SteamAppSteamStorefront) ScrapeInfo(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*SteamAppSteamStorefront) ScrapeReviews

func (s *SteamAppSteamStorefront) ScrapeReviews(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*SteamAppSteamStorefront) ScrapeTags

func (s *SteamAppSteamStorefront) ScrapeTags(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

type SteamAppWrapper

type SteamAppWrapper struct{ SteamApp *SteamApp }

func (*SteamAppWrapper) Default

func (s *SteamAppWrapper) Default()

func (*SteamAppWrapper) Get

func (s *SteamAppWrapper) Get() GameModel[uint64]

func (*SteamAppWrapper) Nil

func (s *SteamAppWrapper) Nil()

func (*SteamAppWrapper) StorefrontScraper

func (s *SteamAppWrapper) StorefrontScraper(storefront Storefront) GameModelStorefrontScraper[uint64]

type Storefront

type Storefront string
const (
	UnknownStorefront Storefront = "U"
	SteamStorefront   Storefront = "S"
	ItchIOStorefront  Storefront = "I"
)

func (Storefront) Index

func (sf Storefront) Index() int

Index returns the index of the Storefront.

func (Storefront) LogoSrc

func (sf Storefront) LogoSrc() string

LogoSrc returns the URL at which the logo for this storefront resides.

func (*Storefront) Scan

func (sf *Storefront) Scan(value interface{}) error

func (Storefront) ScrapeURL

func (sf Storefront) ScrapeURL() urlfmt.URL

ScrapeURL returns the browser.ScrapeURL that can be used to return a standardised version of the URL for a game on this Storefront.

func (Storefront) Storefronts

func (sf Storefront) Storefronts() []Storefront

func (Storefront) String

func (sf Storefront) String() string

String returns the formal name of the Storefront.

func (Storefront) Type

func (sf Storefront) Type() string

func (Storefront) Value

func (sf Storefront) Value() (driver.Value, error)

func (Storefront) Values

func (sf Storefront) Values() []string

type StorefrontConfig

type StorefrontConfig interface {
	StorefrontStorefront() Storefront
	StorefrontTags() TagConfig
}

StorefrontConfig contains the configuration for a specific Storefront.

type StorefrontScrapers

type StorefrontScrapers[ID comparable] struct {
	Timeout time.Duration
	// contains filtered or unexported fields
}

StorefrontScrapers represents a manager for multiple workers that execute calls to ScrapeStorefrontsForGameModel or GameModel.Wrapper (if update is set). Note that this is only valid for jobs of one type of GameModel.

func NewStorefrontScrapers

func NewStorefrontScrapers[ID comparable](
	config ScrapeConfig,
	db *gorm.DB,
	workers, maxConcurrentWorkers, maxJobs int,
	minWorkerWaitTime, maxWorkerWaitTime time.Duration,
) *StorefrontScrapers[ID]

NewStorefrontScrapers creates a new StorefrontScrapers with the given arguments. • config: a ScrapeConfig for use in ScrapeStorefrontsForGameModel.

• db: a reference to a gorm.DB for use in GameModel.Update

• workers: the number of workers to spin up on StorefrontScrapers.Start

• maxConcurrentWorkers: the maximum number of workers that can be executing a job at the same time

• maxJobs: the maximum number of jobs the buffered channel for the jobs can hold

• minWorkerWaitTime: the minimum time a worker should wait after completing a job

• maxWorkerWaitTime: the maximum time a worker should wait after completing a job.

• timeout: the duration after which a worker should time out.

func (*StorefrontScrapers[ID]) Add

func (ss *StorefrontScrapers[ID]) Add(update bool, gameModel GameModel[ID], storefrontIDs map[Storefront]mapset.Set[ID]) (<-chan GameModel[ID], bool)

Add adds a job to be executed by the started workers. This returns a channel that can be read from to block until the game has been scraped, and will also return a boolean value indicating whether the job was queued.

func (*StorefrontScrapers[ID]) Drop

func (ss *StorefrontScrapers[ID]) Drop(no int) int

Drop will try and dequeue the given number of jobs from the StorefrontScrapers. These jobs will just be dropped (i.e. not scraped) and each of their result channels will be filled with a nil value so that any consumers blocking for these channels won't cause a deadlock. Because of this, this method will block until the resulting channels for each dropped job have been filled with nils. The actual number of jobs dropped will be returned.

func (*StorefrontScrapers[ID]) DropAll

func (ss *StorefrontScrapers[ID]) DropAll() int

DropAll works similarly to Drop, but it will instead keep de-queueing jobs from the job channel until there are no more left. Because of this, this method will block until the resulting channels for each dropped job have been filled with nils. It will return the actual number of jobs that have been dropped.

func (*StorefrontScrapers[ID]) Jobs

func (ss *StorefrontScrapers[ID]) Jobs() int

Jobs returns the number of unprocessed jobs left.

func (*StorefrontScrapers[ID]) Start

func (ss *StorefrontScrapers[ID]) Start()

Start the StorefrontScrapers workers.

func (*StorefrontScrapers[ID]) Stop

func (ss *StorefrontScrapers[ID]) Stop()

Stop tries to stop the StorefrontScrapers as fast as possible by first dropping all the remaining jobs in the queue, and then calling Wait to wait for all the currently running workers to finish the jobs they are currently processing.

func (*StorefrontScrapers[ID]) Wait

func (ss *StorefrontScrapers[ID]) Wait()

Wait waits for the workers to finish. It should only be called after all jobs have been added using the Add method. This is because Wait will close the job and guard channels.

type TagConfig

type TagConfig interface {
	TagDefaultValue() float64
	TagUpvotesThreshold() float64
	TagValues() map[string]float64
}

TagConfig contains the configuration for the tags that are found in browser.SteamSpyAppDetails.

type Trend

type Trend struct {
	Model      trendModel
	Regression *regression.Regression
	DataPoints regression.DataPoints
	// contains filtered or unexported fields
}

Trend references the structure used to calculate the regression of the observed variable for a trendModel. This will only work for a regression that has the timestamp of when each trendModel instance was created as a variable that affects the regression's predictions.

func NewTrend

func NewTrend(db *gorm.DB, model trendModel) *Trend

NewTrend creates a new trend and sets the observed value and variables that are used for the regression.

func (*Trend) AddDataPoint

func (trend *Trend) AddDataPoint(date time.Time, score float64, extraVars ...float64)

AddDataPoint will add a data point for the given date (variable) and score (observed value). It can also be given extra variables in case you wanted to do polynomial regression.

func (*Trend) Chart

func (trend *Trend) Chart(width, height int) (image *ChartImage, err error)

Chart generates a chart for the Trend with the given size. The returned bytes.Buffer can be saved as a PNG.

func (*Trend) GetCoeffs

func (trend *Trend) GetCoeffs() (coefficients []float64)

GetCoeffs returns the coefficients for the regression.Regression within this Trend.

func (*Trend) Train

func (trend *Trend) Train() (err error)

Train will add the training data of the model instance to the regression model. This will not run the training itself.

func (*Trend) Trend

func (trend *Trend) Trend() (coefficients []float64, err error)

Trend will run the training for the regression model and return the coefficients for the resulting regression line's formula.

type TrendingDev

type TrendingDev struct {
	Developer *Developer
	Snapshots []*DeveloperSnapshot
	Games     []*Game
	Trend     *Trend
	// contains filtered or unexported fields
}

func (*TrendingDev) GetOutOf

func (td *TrendingDev) GetOutOf() int

func (*TrendingDev) GetPosition

func (td *TrendingDev) GetPosition() int

func (*TrendingDev) HasOutOf

func (td *TrendingDev) HasOutOf() bool

func (*TrendingDev) HasPosition

func (td *TrendingDev) HasPosition() bool

func (*TrendingDev) SetOutOf

func (td *TrendingDev) SetOutOf(outOf int)

func (*TrendingDev) SetPosition

func (td *TrendingDev) SetPosition(pos int)

type UnscrapableStorefront

type UnscrapableStorefront[ID comparable] struct {
	Storefront Storefront
	Source     ScrapableGameSource
}

UnscrapableStorefront is an error that implements the GameModelStorefrontScraper interface so that it can be returned by implementors of the GameModelWrapper.StorefrontScraper in cases where the scrape procedures for a certain Storefront are not yet implemented/will never be implemented.

func (*UnscrapableStorefront[ID]) AfterScrape

func (usw *UnscrapableStorefront[ID]) AfterScrape(args ...any)

func (*UnscrapableStorefront[ID]) Args

func (usw *UnscrapableStorefront[ID]) Args(id ID) []any

func (*UnscrapableStorefront[ID]) Error

func (usw *UnscrapableStorefront[ID]) Error() string

func (*UnscrapableStorefront[ID]) GetStorefront

func (usw *UnscrapableStorefront[ID]) GetStorefront() Storefront

func (*UnscrapableStorefront[ID]) ScrapeCommunity

func (usw *UnscrapableStorefront[ID]) ScrapeCommunity(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*UnscrapableStorefront[ID]) ScrapeExtra

func (usw *UnscrapableStorefront[ID]) ScrapeExtra(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*UnscrapableStorefront[ID]) ScrapeInfo

func (usw *UnscrapableStorefront[ID]) ScrapeInfo(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*UnscrapableStorefront[ID]) ScrapeReviews

func (usw *UnscrapableStorefront[ID]) ScrapeReviews(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

func (*UnscrapableStorefront[ID]) ScrapeTags

func (usw *UnscrapableStorefront[ID]) ScrapeTags(config ScrapeConfig, maxTries int, minDelay time.Duration, args ...any) error

Jump to

Keyboard shortcuts

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