Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions cmd/generate/codegen_go.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Code generators for the server-side Go: game/generated_{constants,score,score_summary,
// ranking_fields}.go. Each emits the gofmt'd output of a templates_go/*.go.tmpl executed against
// the view model (see viewmodel.go). The matching test generators live in codegen_go_tests.go;
// the web UI generators in codegen_web.go.

package main

import "path/filepath"

// phaseFieldPrefix maps a phase to its Go field-name prefix. It's the single source for that mapping,
// used by the `phasePrefix` template helper (template_funcs.go) and by the CountFields joins in
// buildTemplateData (viewmodel.go).
var phaseFieldPrefix = map[string]string{"auto": "Auto", "teleop": "Teleop", "endgame": "Endgame"}

// generateConstants emits game/generated_constants.go — game-wide metadata, mode flags, foul points.
func generateConstants(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("constants.go.tmpl", filepath.Join(destDir, "generated_constants.go"), buildTemplateData(yamlData))
}

// generateScore emits game/generated_score.go — the Score struct, Equals, the Phase enum, and the
// Adjust/Set/Cycle scoring methods + dispatchers.
func generateScore(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("score.go.tmpl", filepath.Join(destDir, "generated_score.go"), buildTemplateData(yamlData))
}

// generateScoreSummary emits game/generated_score_summary.go — ScoreSummary, per-phase point
// accumulation, and the DetermineMatchStatus tiebreak cascade.
func generateScoreSummary(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("score_summary.go.tmpl", filepath.Join(destDir, "generated_score_summary.go"), buildTemplateData(yamlData))
}

// generateRankingFields emits game/generated_ranking_fields.go — RankingFields, AddScoreSummary,
// and the Less tiebreaker cascade.
func generateRankingFields(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("ranking_fields.go.tmpl", filepath.Join(destDir, "generated_ranking_fields.go"), buildTemplateData(yamlData))
}
21 changes: 21 additions & 0 deletions cmd/generate/codegen_go_tests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Generators for the tests of the server-side Go (codegen_go.go) — one generated test file per
// generated source file. Each emits the gofmt'd output of a templates_go/*_test.go.tmpl.

package main

import "path/filepath"

// generateScoreTest emits game/generated_score_test.go — Score mutators + Equals.
func generateScoreTest(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("score_test.go.tmpl", filepath.Join(destDir, "generated_score_test.go"), buildTemplateData(yamlData))
}

// generateScoreSummaryTest emits game/generated_score_summary_test.go — point math + tiebreaks.
func generateScoreSummaryTest(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("score_summary_test.go.tmpl", filepath.Join(destDir, "generated_score_summary_test.go"), buildTemplateData(yamlData))
}

// generateRankingFieldsTest emits game/generated_ranking_fields_test.go — the ranking-Less cascade.
func generateRankingFieldsTest(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("ranking_fields_test.go.tmpl", filepath.Join(destDir, "generated_ranking_fields_test.go"), buildTemplateData(yamlData))
}
112 changes: 112 additions & 0 deletions cmd/generate/codegen_web.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Code generators for the web UI surfaces — scoring panel, referee panel, and audience display
// (HTML + JS) — produced by executing the committed templates/static custom_*.tmpl sources. The
// test generator for these surfaces lives in codegen_web_tests.go; the server Go in codegen_go.go.

package main

import "path/filepath"

// ScoringBucket is one rolled-up scoring group — a ScoreSummary point field and an audience-display
// entry. ID/DisplayName are the resolved, presentable label; CountIDs are the scoring counts
// merged into this bucket.
type ScoringBucket struct {
ID string
DisplayName string
CountIDs []string
}

// buildScoringGroups resolves every scoring count into the bucket it rolls up into, merging counts
// that land in the same bucket. A count with a scoring_group joins that group's bucket; a count with
// no scoring_group stands alone as its own bucket under its own id. (game_piece is piece identity,
// not a rollup — to group counts, give them a shared scoring_group.)
//
// The lookup key is tagged by source ("sg:"/"ct:") so a scoring_group and an ungrouped count that
// happen to share the same id string aren't merged into one bucket by accident.
func buildScoringGroups(yamlData *GameYAML) []ScoringBucket {
var buckets []ScoringBucket
seen := make(map[string]int) // lookup key -> index in buckets, so repeats merge into one bucket

for _, sc := range yamlData.ScoringCounts {
var lookupKey, groupID, displayName string
if sc.ScoringGroup != "" { // grouped: roll up with the other counts in this scoring_group
lookupKey = "sg:" + sc.ScoringGroup
groupID = sc.ScoringGroup
displayName = sc.ScoringGroup
for _, group := range yamlData.ScoringGroups {
if group.ID == sc.ScoringGroup {
displayName = group.DisplayName // prefer the group's label over its raw id
}
}
} else { // ungrouped: the count is its own bucket
lookupKey = "ct:" + sc.ID
groupID = sc.ID
displayName = sc.DisplayName
}

if idx, ok := seen[lookupKey]; ok {
buckets[idx].CountIDs = append(buckets[idx].CountIDs, sc.ID)
} else {
seen[lookupKey] = len(buckets)
buckets = append(buckets, ScoringBucket{ID: groupID, DisplayName: displayName, CountIDs: []string{sc.ID}})
}
}

return buckets
}

var phaseSectionTitle = map[string]string{"auto": "Auto", "teleop": "Teleop", "endgame": "Endgame"}

// generateReportsRankings emits web/generated_reports_rankings_custom.go — the CSV/PDF qualification
// rankings report handlers, whose middle columns track the configured ranking_tiebreakers so the
// report never references a RankingFields column the current custom_game.yaml doesn't generate.
func generateReportsRankings(yamlData *GameYAML, webDir string) error {
return renderGoTemplate("reports_rankings_custom.go.tmpl", filepath.Join(webDir, "generated_reports_rankings_custom.go"), buildTemplateData(yamlData))
}

func generateScoringPanelTemplate(yamlData *GameYAML, templatesDir string) error {
return renderWebTemplate(
filepath.Join(templatesDir, "custom_scoring_panel.html.tmpl"),
filepath.Join(templatesDir, "generated_scoring_panel.html"),
buildTemplateData(yamlData),
)
}

func generateScoringPanelJS(yamlData *GameYAML, staticJsDir string) error {
return renderWebTemplate(
filepath.Join(staticJsDir, "custom_scoring_panel.js.tmpl"),
filepath.Join(staticJsDir, "generated_scoring_panel.js"),
buildTemplateData(yamlData),
)
}

func generateAudienceDisplayTemplate(yamlData *GameYAML, templatesDir string) error {
return renderWebTemplate(
filepath.Join(templatesDir, "custom_audience_display.html.tmpl"),
filepath.Join(templatesDir, "generated_audience_display.html"),
buildTemplateData(yamlData),
)
}

func generateAudienceDisplayJS(yamlData *GameYAML, staticJsDir string) error {
return renderWebTemplate(
filepath.Join(staticJsDir, "custom_audience_display.js.tmpl"),
filepath.Join(staticJsDir, "generated_audience_display.js"),
buildTemplateData(yamlData),
)
}

func generateRefereePanelTemplate(yamlData *GameYAML, templatesDir string) error {
return renderWebTemplate(
filepath.Join(templatesDir, "custom_referee_panel.html.tmpl"),
filepath.Join(templatesDir, "generated_referee_panel.html"),
buildTemplateData(yamlData),
)
}

func generateRefereePanelJS(yamlData *GameYAML, staticJsDir string) error {
return renderWebTemplate(
filepath.Join(staticJsDir, "custom_referee_panel.js.tmpl"),
filepath.Join(staticJsDir, "generated_referee_panel.js"),
buildTemplateData(yamlData),
)
}
24 changes: 24 additions & 0 deletions cmd/generate/codegen_web_tests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Generator for the test of the web UI surfaces (codegen_web.go): emits
// cmd/generate/generated_template_test.go, which asserts the rendered templates parse and contain
// the expected per-element markup.

package main

import "path/filepath"

func generateTemplateTest(yamlData *GameYAML, destDir string) error {
return renderGoTemplate("template_test.go.tmpl", filepath.Join(destDir, "generated_template_test.go"), buildTemplateData(yamlData))
}

// generateReportsRankingsTest emits web/generated_reports_rankings_custom_test.go — the CSV/PDF
// rankings report tests, whose fixtures and expected CSV are built from the configured tiebreakers.
func generateReportsRankingsTest(yamlData *GameYAML, webDir string) error {
return renderGoTemplate("reports_rankings_custom_test.go.tmpl", filepath.Join(webDir, "generated_reports_rankings_custom_test.go"), buildTemplateData(yamlData))
}

// generateQualificationRankingsTest emits tournament/generated_qualification_rankings_custom_test.go
// — the end-to-end CalculateRankings test, which grants points via the first scoring count the
// current custom_game.yaml declares rather than a hard-coded field name.
func generateQualificationRankingsTest(yamlData *GameYAML, tournamentDir string) error {
return renderGoTemplate("qualification_rankings_custom_test.go.tmpl", filepath.Join(tournamentDir, "generated_qualification_rankings_custom_test.go"), buildTemplateData(yamlData))
}
Loading